Using public API for SemanticVersion (paulevsGitch/BCLib#146)

This commit is contained in:
Frank 2022-06-07 16:51:26 +02:00
parent 69a4583d57
commit c1b5b7c01b

View file

@ -1,21 +1,12 @@
package ru.bclib.util; package ru.bclib.util;
import net.fabricmc.loader.api.*;
import net.fabricmc.loader.api.metadata.*;
import com.google.gson.JsonElement; import com.google.gson.JsonElement;
import com.google.gson.JsonObject; import com.google.gson.JsonObject;
import com.google.gson.JsonParser; import com.google.gson.JsonParser;
import com.google.gson.stream.JsonReader; import com.google.gson.stream.JsonReader;
import net.fabricmc.loader.api.FabricLoader;
import net.fabricmc.loader.api.ModContainer;
import net.fabricmc.loader.api.SemanticVersion;
import net.fabricmc.loader.api.Version;
import net.fabricmc.loader.api.VersionParsingException;
import net.fabricmc.loader.api.metadata.ContactInformation;
import net.fabricmc.loader.api.metadata.CustomValue;
import net.fabricmc.loader.api.metadata.ModDependency;
import net.fabricmc.loader.api.metadata.ModEnvironment;
import net.fabricmc.loader.api.metadata.ModMetadata;
import net.fabricmc.loader.api.metadata.Person;
import net.fabricmc.loader.util.version.SemanticVersionImpl;
import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.LogManager;
import ru.bclib.BCLib; import ru.bclib.BCLib;
@ -28,429 +19,414 @@ import java.nio.file.FileSystem;
import java.nio.file.FileSystems; import java.nio.file.FileSystems;
import java.nio.file.Files; import java.nio.file.Files;
import java.nio.file.Path; import java.nio.file.Path;
import java.util.ArrayList; import java.util.*;
import java.util.Collection;
import java.util.HashMap;
import java.util.Locale;
import java.util.Map;
import java.util.Optional;
import java.util.regex.Matcher; import java.util.regex.Matcher;
import java.util.regex.Pattern; import java.util.regex.Pattern;
public class ModUtil { public class ModUtil {
private static Map<String, ModInfo> mods; private static Map<String, ModInfo> mods;
/** /**
* Unloads the cache of available mods created from {@link #getMods()} * Unloads the cache of available mods created from {@link #getMods()}
*/ */
public static void invalidateCachedMods() { public static void invalidateCachedMods() {
mods = null; mods = null;
} }
/** /**
* return a map of all mods that were found in the 'mods'-folder. * return a map of all mods that were found in the 'mods'-folder.
* <p> * <p>
* The method will cache the results. You can clear that cache (and free the memory) by * The method will cache the results. You can clear that cache (and free the memory) by
* calling {@link #invalidateCachedMods()} * calling {@link #invalidateCachedMods()}
* <p> * <p>
* An error message is printed if a mod fails to load, but the parsing will continue. * An error message is printed if a mod fails to load, but the parsing will continue.
* *
* @return A map of all found mods. (key=ModID, value={@link ModInfo}) * @return A map of all found mods. (key=ModID, value={@link ModInfo})
*/ */
public static Map<String, ModInfo> getMods() { public static Map<String, ModInfo> getMods() {
if (mods != null) return mods; if (mods != null) return mods;
mods = new HashMap<>(); mods = new HashMap<>();
org.apache.logging.log4j.Logger logger = LogManager.getFormatterLogger("BCLib|ModLoader"); org.apache.logging.log4j.Logger logger = LogManager.getFormatterLogger("BCLib|ModLoader");
PathUtil.fileWalker(PathUtil.MOD_FOLDER.toFile(), false, (ModUtil::accept)); PathUtil.fileWalker(PathUtil.MOD_FOLDER.toFile(), false, (ModUtil::accept));
return mods; return mods;
} }
private static ModMetadata readJSON(InputStream is, String sourceFile) throws IOException { private static ModMetadata readJSON(InputStream is, String sourceFile) throws IOException {
try (com.google.gson.stream.JsonReader reader = new JsonReader(new InputStreamReader(is, StandardCharsets.UTF_8))) { try (com.google.gson.stream.JsonReader reader = new JsonReader(new InputStreamReader(is,
JsonObject data = new JsonParser().parse(reader) StandardCharsets.UTF_8))) {
.getAsJsonObject(); JsonObject data = new JsonParser().parse(reader)
Version ver; .getAsJsonObject();
try { Version ver;
ver = new SemanticVersionImpl(data.get("version") try {
.getAsString(), false); ver = SemanticVersion.parse(data.get("version").getAsString());
} } catch (VersionParsingException e) {
catch (VersionParsingException e) { BCLib.LOGGER.error("Unable to parse Version in " + sourceFile);
BCLib.LOGGER.error("Unable to parse Version in " + sourceFile); return null;
return null; }
}
if (data.get("id") == null) { if (data.get("id") == null) {
BCLib.LOGGER.error("Unable to read ID in " + sourceFile); BCLib.LOGGER.error("Unable to read ID in " + sourceFile);
return null; return null;
} }
if (data.get("name") == null) { if (data.get("name") == null) {
BCLib.LOGGER.error("Unable to read name in " + sourceFile); BCLib.LOGGER.error("Unable to read name in " + sourceFile);
return null; return null;
} }
return new ModMetadata() { return new ModMetadata() {
@Override @Override
public Version getVersion() { public Version getVersion() {
return ver; return ver;
} }
@Override @Override
public String getType() { public String getType() {
return "fabric"; return "fabric";
} }
@Override @Override
public String getId() { public String getId() {
return data.get("id") return data.get("id")
.getAsString(); .getAsString();
} }
@Override @Override
public Collection<String> getProvides() { public Collection<String> getProvides() {
return new ArrayList<>(); return new ArrayList<>();
} }
@Override @Override
public ModEnvironment getEnvironment() { public ModEnvironment getEnvironment() {
JsonElement env = data.get("environment"); JsonElement env = data.get("environment");
if (env == null) { if (env == null) {
BCLib.LOGGER.warning("No environment specified in " + sourceFile); BCLib.LOGGER.warning("No environment specified in " + sourceFile);
//return ModEnvironment.UNIVERSAL; //return ModEnvironment.UNIVERSAL;
} }
final String environment = env == null ? "" : env.getAsString() final String environment = env == null ? "" : env.getAsString()
.toLowerCase(Locale.ROOT); .toLowerCase(Locale.ROOT);
if (environment.isEmpty() || environment.equals("*") || environment.equals("\"*\"") || environment.equals("common")) { if (environment.isEmpty() || environment.equals("*") || environment.equals("\"*\"") || environment.equals(
JsonElement entrypoints = data.get("entrypoints"); "common")) {
boolean hasClient = true; JsonElement entrypoints = data.get("entrypoints");
boolean hasClient = true;
//check if there is an actual client entrypoint //check if there is an actual client entrypoint
if (entrypoints != null && entrypoints.isJsonObject()) { if (entrypoints != null && entrypoints.isJsonObject()) {
JsonElement client = entrypoints.getAsJsonObject() JsonElement client = entrypoints.getAsJsonObject()
.get("client"); .get("client");
if (client != null && client.isJsonArray()) { if (client != null && client.isJsonArray()) {
hasClient = client.getAsJsonArray() hasClient = client.getAsJsonArray()
.size() > 0; .size() > 0;
} } else if (client == null || !client.isJsonPrimitive()) {
else if (client == null || !client.isJsonPrimitive()) { hasClient = false;
hasClient = false; } else if (!client.getAsJsonPrimitive()
} .isString()) {
else if (!client.getAsJsonPrimitive() hasClient = false;
.isString()) { }
hasClient = false; }
}
}
//if (hasClient == false) return ModEnvironment.SERVER; //if (hasClient == false) return ModEnvironment.SERVER;
return ModEnvironment.UNIVERSAL; return ModEnvironment.UNIVERSAL;
} } else if (environment.equals("client")) {
else if (environment.equals("client")) { return ModEnvironment.CLIENT;
return ModEnvironment.CLIENT; } else if (environment.equals("server")) {
} return ModEnvironment.SERVER;
else if (environment.equals("server")) { } else {
return ModEnvironment.SERVER; BCLib.LOGGER.error("Unable to read environment in " + sourceFile);
} return ModEnvironment.UNIVERSAL;
else { }
BCLib.LOGGER.error("Unable to read environment in " + sourceFile); }
return ModEnvironment.UNIVERSAL;
}
}
@Override @Override
public Collection<ModDependency> getDepends() { public Collection<ModDependency> getDepends() {
return new ArrayList<>(); return new ArrayList<>();
} }
@Override @Override
public Collection<ModDependency> getRecommends() { public Collection<ModDependency> getRecommends() {
return new ArrayList<>(); return new ArrayList<>();
} }
@Override @Override
public Collection<ModDependency> getSuggests() { public Collection<ModDependency> getSuggests() {
return new ArrayList<>(); return new ArrayList<>();
} }
@Override @Override
public Collection<ModDependency> getConflicts() { public Collection<ModDependency> getConflicts() {
return new ArrayList<>(); return new ArrayList<>();
} }
@Override @Override
public Collection<ModDependency> getBreaks() { public Collection<ModDependency> getBreaks() {
return new ArrayList<>(); return new ArrayList<>();
} }
public Collection<ModDependency> getDependencies() { public Collection<ModDependency> getDependencies() {
return new ArrayList<>(); return new ArrayList<>();
} }
@Override @Override
public String getName() { public String getName() {
return data.get("name") return data.get("name")
.getAsString(); .getAsString();
} }
@Override @Override
public String getDescription() { public String getDescription() {
return ""; return "";
} }
@Override @Override
public Collection<Person> getAuthors() { public Collection<Person> getAuthors() {
return new ArrayList<>(); return new ArrayList<>();
} }
@Override @Override
public Collection<Person> getContributors() { public Collection<Person> getContributors() {
return new ArrayList<>(); return new ArrayList<>();
} }
@Override @Override
public ContactInformation getContact() { public ContactInformation getContact() {
return null; return null;
} }
@Override @Override
public Collection<String> getLicense() { public Collection<String> getLicense() {
return new ArrayList<>(); return new ArrayList<>();
} }
@Override @Override
public Optional<String> getIconPath(int size) { public Optional<String> getIconPath(int size) {
return Optional.empty(); return Optional.empty();
} }
@Override @Override
public boolean containsCustomValue(String key) { public boolean containsCustomValue(String key) {
return false; return false;
} }
@Override @Override
public CustomValue getCustomValue(String key) { public CustomValue getCustomValue(String key) {
return null; return null;
} }
@Override @Override
public Map<String, CustomValue> getCustomValues() { public Map<String, CustomValue> getCustomValues() {
return new HashMap<>(); return new HashMap<>();
} }
@Override @Override
public boolean containsCustomElement(String key) { public boolean containsCustomElement(String key) {
return false; return false;
} }
public JsonElement getCustomElement(String key) { public JsonElement getCustomElement(String key) {
return null; return null;
} }
}; };
} }
} }
/** /**
* Returns the {@link ModInfo} or {@code null} if the mod was not found. * Returns the {@link ModInfo} or {@code null} if the mod was not found.
* <p> * <p>
* The call will also return null if the mode-Version in the jar-File is not the same * The call will also return null if the mode-Version in the jar-File is not the same
* as the version of the loaded Mod. * as the version of the loaded Mod.
* *
* @param modID The mod ID to query * @param modID The mod ID to query
* @return A {@link ModInfo}-Object for the querried Mod. * @return A {@link ModInfo}-Object for the querried Mod.
*/ */
public static ModInfo getModInfo(String modID) { public static ModInfo getModInfo(String modID) {
return getModInfo(modID, true); return getModInfo(modID, true);
} }
public static ModInfo getModInfo(String modID, boolean matchVersion) { public static ModInfo getModInfo(String modID, boolean matchVersion) {
getMods(); getMods();
final ModInfo mi = mods.get(modID); final ModInfo mi = mods.get(modID);
if (mi == null || (matchVersion && !getModVersion(modID).equals(mi.getVersion()))) return null; if (mi == null || (matchVersion && !getModVersion(modID).equals(mi.getVersion()))) return null;
return mi; return mi;
} }
/** /**
* Local Mod Version for the queried Mod * Local Mod Version for the queried Mod
* *
* @param modID The mod ID to query * @param modID The mod ID to query
* @return The version of the locally installed Mod * @return The version of the locally installed Mod
*/ */
public static String getModVersion(String modID) { public static String getModVersion(String modID) {
Optional<ModContainer> optional = FabricLoader.getInstance() Optional<ModContainer> optional = FabricLoader.getInstance()
.getModContainer(modID); .getModContainer(modID);
if (optional.isPresent()) { if (optional.isPresent()) {
ModContainer modContainer = optional.get(); ModContainer modContainer = optional.get();
return ModInfo.versionToString(modContainer.getMetadata() return ModInfo.versionToString(modContainer.getMetadata()
.getVersion()); .getVersion());
} }
return getModVersionFromJar(modID); return getModVersionFromJar(modID);
} }
/** /**
* Local Mod Version for the queried Mod from the Jar-File in the games mod-directory * Local Mod Version for the queried Mod from the Jar-File in the games mod-directory
* *
* @param modID The mod ID to query * @param modID The mod ID to query
* @return The version of the locally installed Mod * @return The version of the locally installed Mod
*/ */
public static String getModVersionFromJar(String modID) { public static String getModVersionFromJar(String modID) {
final ModInfo mi = getModInfo(modID, false); final ModInfo mi = getModInfo(modID, false);
if (mi != null) return mi.getVersion(); if (mi != null) return mi.getVersion();
return "0.0.0"; return "0.0.0";
} }
/** /**
* Get mod version from string. String should be in format: %d.%d.%d * Get mod version from string. String should be in format: %d.%d.%d
* *
* @param version - {@link String} mod version. * @param version - {@link String} mod version.
* @return int mod version. * @return int mod version.
*/ */
public static int convertModVersion(String version) { public static int convertModVersion(String version) {
if (version.isEmpty()) { if (version.isEmpty()) {
return 0; return 0;
} }
try { try {
int res = 0; int res = 0;
final String semanticVersionPattern = "(\\d+)\\.(\\d+)(\\.(\\d+))?\\D*"; final String semanticVersionPattern = "(\\d+)\\.(\\d+)(\\.(\\d+))?\\D*";
final Matcher matcher = Pattern.compile(semanticVersionPattern) final Matcher matcher = Pattern.compile(semanticVersionPattern)
.matcher(version); .matcher(version);
if (matcher.find()) { if (matcher.find()) {
if (matcher.groupCount() > 0) if (matcher.groupCount() > 0)
res = matcher.group(1) == null ? 0 : ((Integer.parseInt(matcher.group(1)) & 0xFF) << 22); res = matcher.group(1) == null ? 0 : ((Integer.parseInt(matcher.group(1)) & 0xFF) << 22);
if (matcher.groupCount() > 1) if (matcher.groupCount() > 1)
res |= matcher.group(2) == null ? 0 : ((Integer.parseInt(matcher.group(2)) & 0xFF) << 14); res |= matcher.group(2) == null ? 0 : ((Integer.parseInt(matcher.group(2)) & 0xFF) << 14);
if (matcher.groupCount() > 3) if (matcher.groupCount() > 3)
res |= matcher.group(4) == null ? 0 : Integer.parseInt(matcher.group(4)) & 0x3FFF; res |= matcher.group(4) == null ? 0 : Integer.parseInt(matcher.group(4)) & 0x3FFF;
} }
return res; return res;
} } catch (Exception e) {
catch (Exception e) { return 0;
return 0; }
} }
}
/** /**
* Get mod version from integer. String will be in format %d.%d.%d * Get mod version from integer. String will be in format %d.%d.%d
* *
* @param version - mod version in integer form. * @param version - mod version in integer form.
* @return {@link String} mod version. * @return {@link String} mod version.
*/ */
public static String convertModVersion(int version) { public static String convertModVersion(int version) {
int a = (version >> 22) & 0xFF; int a = (version >> 22) & 0xFF;
int b = (version >> 14) & 0xFF; int b = (version >> 14) & 0xFF;
int c = version & 0x3FFF; int c = version & 0x3FFF;
return String.format(Locale.ROOT, "%d.%d.%d", a, b, c); return String.format(Locale.ROOT, "%d.%d.%d", a, b, c);
} }
/** /**
* {@code true} if the version v1 is larger than v2 * {@code true} if the version v1 is larger than v2
* *
* @param v1 A Version string * @param v1 A Version string
* @param v2 Another Version string * @param v2 Another Version string
* @return v1 &gt; v2 * @return v1 &gt; v2
*/ */
public static boolean isLargerVersion(String v1, String v2) { public static boolean isLargerVersion(String v1, String v2) {
return convertModVersion(v1) > convertModVersion(v2); return convertModVersion(v1) > convertModVersion(v2);
} }
/** /**
* {@code true} if the version v1 is larger or equal v2 * {@code true} if the version v1 is larger or equal v2
* *
* @param v1 A Version string * @param v1 A Version string
* @param v2 Another Version string * @param v2 Another Version string
* @return v1 &ge; v2 * @return v1 &ge; v2
*/ */
public static boolean isLargerOrEqualVersion(String v1, String v2) { public static boolean isLargerOrEqualVersion(String v1, String v2) {
return convertModVersion(v1) >= convertModVersion(v2); return convertModVersion(v1) >= convertModVersion(v2);
} }
private static void accept(Path file) { private static void accept(Path file) {
try { try {
URI uri = URI.create("jar:" + file.toUri()); URI uri = URI.create("jar:" + file.toUri());
FileSystem fs; FileSystem fs;
// boolean doClose = false; // boolean doClose = false;
try { try {
fs = FileSystems.getFileSystem(uri); fs = FileSystems.getFileSystem(uri);
} } catch (Exception e) {
catch (Exception e) { // doClose = true;
// doClose = true; fs = FileSystems.newFileSystem(file);
fs = FileSystems.newFileSystem(file); }
} if (fs != null) {
if (fs != null) { try {
try { Path modMetaFile = fs.getPath("fabric.mod.json");
Path modMetaFile = fs.getPath("fabric.mod.json"); if (modMetaFile != null) {
if (modMetaFile != null) { try (InputStream is = Files.newInputStream(modMetaFile)) {
try (InputStream is = Files.newInputStream(modMetaFile)) { //ModMetadata mc = ModMetadataParser.parseMetadata(is, uri.toString(), new LinkedList<String>());
//ModMetadata mc = ModMetadataParser.parseMetadata(is, uri.toString(), new LinkedList<String>()); ModMetadata mc = readJSON(is, uri.toString());
ModMetadata mc = readJSON(is, uri.toString()); if (mc != null) {
if (mc != null) { mods.put(mc.getId(), new ModInfo(mc, file));
mods.put(mc.getId(), new ModInfo(mc, file)); }
} }
} }
} } catch (Exception e) {
} BCLib.LOGGER.error("Error for " + uri + ": " + e.toString());
catch (Exception e) { }
BCLib.LOGGER.error("Error for " + uri + ": " + e.toString()); //if (doClose) fs.close();
} }
//if (doClose) fs.close(); } catch (Exception e) {
} BCLib.LOGGER.error("Error for " + file.toUri() + ": " + e.toString());
} e.printStackTrace();
catch (Exception e) { }
BCLib.LOGGER.error("Error for " + file.toUri() + ": " + e.toString()); }
e.printStackTrace();
}
}
public static class ModInfo { public static class ModInfo {
public final ModMetadata metadata; public final ModMetadata metadata;
public final Path jarPath; public final Path jarPath;
ModInfo(ModMetadata metadata, Path jarPath) { ModInfo(ModMetadata metadata, Path jarPath) {
this.metadata = metadata; this.metadata = metadata;
this.jarPath = jarPath; this.jarPath = jarPath;
} }
public static String versionToString(Version v) { public static String versionToString(Version v) {
if (v instanceof SemanticVersion) { if (v instanceof SemanticVersion) {
return versionToString((SemanticVersion) v); return versionToString((SemanticVersion) v);
} }
return convertModVersion(convertModVersion(v.toString())); return convertModVersion(convertModVersion(v.toString()));
} }
public static String versionToString(SemanticVersion v) { public static String versionToString(SemanticVersion v) {
StringBuilder stringBuilder = new StringBuilder(); StringBuilder stringBuilder = new StringBuilder();
boolean first = true; boolean first = true;
final int cCount = Math.min(v.getVersionComponentCount(), 3); final int cCount = Math.min(v.getVersionComponentCount(), 3);
for (int i = 0; i < cCount; i++) { for (int i = 0; i < cCount; i++) {
if (first) { if (first) {
first = false; first = false;
} } else {
else { stringBuilder.append('.');
stringBuilder.append('.'); }
}
stringBuilder.append(v.getVersionComponent(i)); stringBuilder.append(v.getVersionComponent(i));
} }
return stringBuilder.toString(); return stringBuilder.toString();
} }
@Override @Override
public String toString() { public String toString() {
return "ModInfo{" + "id=" + metadata.getId() + ", version=" + metadata.getVersion() + ", jarPath=" + jarPath + '}'; return "ModInfo{" + "id=" + metadata.getId() + ", version=" + metadata.getVersion() + ", jarPath=" + jarPath + '}';
} }
public String getVersion() { public String getVersion() {
if (metadata == null) { if (metadata == null) {
return "0.0.0"; return "0.0.0";
} }
return versionToString(metadata.getVersion()); return versionToString(metadata.getVersion());
} }
} }
} }