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(
if (environment.isEmpty() || environment.equals("*") || environment.equals("\"*\"") || environment.equals("common")) { "common")) {
JsonElement entrypoints = data.get("entrypoints"); JsonElement entrypoints = data.get("entrypoints");
boolean hasClient = true; 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;
return ModEnvironment.UNIVERSAL;
//if (hasClient == false) return ModEnvironment.SERVER; } else if (environment.equals("client")) {
return ModEnvironment.UNIVERSAL; return ModEnvironment.CLIENT;
} } else if (environment.equals("server")) {
else if (environment.equals("client")) { return ModEnvironment.SERVER;
return ModEnvironment.CLIENT; } else {
} BCLib.LOGGER.error("Unable to read environment in " + sourceFile);
else if (environment.equals("server")) { return ModEnvironment.UNIVERSAL;
return ModEnvironment.SERVER; }
} }
else {
BCLib.LOGGER.error("Unable to read environment in " + sourceFile); @Override
return ModEnvironment.UNIVERSAL; public Collection<ModDependency> getDepends() {
} return new ArrayList<>();
} }
@Override @Override
public Collection<ModDependency> getDepends() { public Collection<ModDependency> getRecommends() {
return new ArrayList<>(); return new ArrayList<>();
} }
@Override @Override
public Collection<ModDependency> getRecommends() { public Collection<ModDependency> getSuggests() {
return new ArrayList<>(); return new ArrayList<>();
} }
@Override @Override
public Collection<ModDependency> getSuggests() { public Collection<ModDependency> getConflicts() {
return new ArrayList<>(); return new ArrayList<>();
} }
@Override @Override
public Collection<ModDependency> getConflicts() { public Collection<ModDependency> getBreaks() {
return new ArrayList<>(); return new ArrayList<>();
} }
@Override public Collection<ModDependency> getDependencies() {
public Collection<ModDependency> getBreaks() { return new ArrayList<>();
return new ArrayList<>(); }
}
@Override
public Collection<ModDependency> getDependencies() { public String getName() {
return new ArrayList<>(); return data.get("name")
} .getAsString();
}
@Override
public String getName() { @Override
return data.get("name") public String getDescription() {
.getAsString(); return "";
} }
@Override @Override
public String getDescription() { public Collection<Person> getAuthors() {
return ""; return new ArrayList<>();
} }
@Override @Override
public Collection<Person> getAuthors() { public Collection<Person> getContributors() {
return new ArrayList<>(); return new ArrayList<>();
} }
@Override @Override
public Collection<Person> getContributors() { public ContactInformation getContact() {
return new ArrayList<>(); return null;
} }
@Override @Override
public ContactInformation getContact() { public Collection<String> getLicense() {
return null; return new ArrayList<>();
} }
@Override @Override
public Collection<String> getLicense() { public Optional<String> getIconPath(int size) {
return new ArrayList<>(); return Optional.empty();
} }
@Override @Override
public Optional<String> getIconPath(int size) { public boolean containsCustomValue(String key) {
return Optional.empty(); return false;
} }
@Override @Override
public boolean containsCustomValue(String key) { public CustomValue getCustomValue(String key) {
return false; return null;
} }
@Override @Override
public CustomValue getCustomValue(String key) { public Map<String, CustomValue> getCustomValues() {
return null; return new HashMap<>();
} }
@Override @Override
public Map<String, CustomValue> getCustomValues() { public boolean containsCustomElement(String key) {
return new HashMap<>(); return false;
} }
@Override public JsonElement getCustomElement(String key) {
public boolean containsCustomElement(String key) { return null;
return false; }
} };
}
public JsonElement getCustomElement(String key) { }
return null;
} /**
}; * Returns the {@link ModInfo} or {@code null} if the mod was not found.
} * <p>
} * 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.
/** *
* Returns the {@link ModInfo} or {@code null} if the mod was not found. * @param modID The mod ID to query
* <p> * @return A {@link ModInfo}-Object for the querried Mod.
* 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. public static ModInfo getModInfo(String modID) {
* return getModInfo(modID, true);
* @param modID The mod ID to query }
* @return A {@link ModInfo}-Object for the querried Mod.
*/ public static ModInfo getModInfo(String modID, boolean matchVersion) {
public static ModInfo getModInfo(String modID) { getMods();
return getModInfo(modID, true); final ModInfo mi = mods.get(modID);
} if (mi == null || (matchVersion && !getModVersion(modID).equals(mi.getVersion()))) return null;
return mi;
public static ModInfo getModInfo(String modID, boolean matchVersion) { }
getMods();
final ModInfo mi = mods.get(modID); /**
if (mi == null || (matchVersion && !getModVersion(modID).equals(mi.getVersion()))) return null; * Local Mod Version for the queried Mod
return mi; *
} * @param modID The mod ID to query
* @return The version of the locally installed Mod
/** */
* Local Mod Version for the queried Mod public static String getModVersion(String modID) {
* Optional<ModContainer> optional = FabricLoader.getInstance()
* @param modID The mod ID to query .getModContainer(modID);
* @return The version of the locally installed Mod if (optional.isPresent()) {
*/ ModContainer modContainer = optional.get();
public static String getModVersion(String modID) { return ModInfo.versionToString(modContainer.getMetadata()
Optional<ModContainer> optional = FabricLoader.getInstance() .getVersion());
.getModContainer(modID);
if (optional.isPresent()) { }
ModContainer modContainer = optional.get();
return ModInfo.versionToString(modContainer.getMetadata() return getModVersionFromJar(modID);
.getVersion()); }
} /**
* Local Mod Version for the queried Mod from the Jar-File in the games mod-directory
return getModVersionFromJar(modID); *
} * @param modID The mod ID to query
* @return The version of the locally installed Mod
/** */
* Local Mod Version for the queried Mod from the Jar-File in the games mod-directory public static String getModVersionFromJar(String modID) {
* final ModInfo mi = getModInfo(modID, false);
* @param modID The mod ID to query if (mi != null) return mi.getVersion();
* @return The version of the locally installed Mod
*/ return "0.0.0";
public static String getModVersionFromJar(String modID) { }
final ModInfo mi = getModInfo(modID, false);
if (mi != null) return mi.getVersion(); /**
* Get mod version from string. String should be in format: %d.%d.%d
return "0.0.0"; *
} * @param version - {@link String} mod version.
* @return int mod version.
/** */
* Get mod version from string. String should be in format: %d.%d.%d public static int convertModVersion(String version) {
* if (version.isEmpty()) {
* @param version - {@link String} mod version. return 0;
* @return int mod version. }
*/ try {
public static int convertModVersion(String version) { int res = 0;
if (version.isEmpty()) { final String semanticVersionPattern = "(\\d+)\\.(\\d+)(\\.(\\d+))?\\D*";
return 0; final Matcher matcher = Pattern.compile(semanticVersionPattern)
} .matcher(version);
try { if (matcher.find()) {
int res = 0; if (matcher.groupCount() > 0)
final String semanticVersionPattern = "(\\d+)\\.(\\d+)(\\.(\\d+))?\\D*"; res = matcher.group(1) == null ? 0 : ((Integer.parseInt(matcher.group(1)) & 0xFF) << 22);
final Matcher matcher = Pattern.compile(semanticVersionPattern) if (matcher.groupCount() > 1)
.matcher(version); res |= matcher.group(2) == null ? 0 : ((Integer.parseInt(matcher.group(2)) & 0xFF) << 14);
if (matcher.find()) { if (matcher.groupCount() > 3)
if (matcher.groupCount() > 0) res |= matcher.group(4) == null ? 0 : Integer.parseInt(matcher.group(4)) & 0x3FFF;
res = matcher.group(1) == null ? 0 : ((Integer.parseInt(matcher.group(1)) & 0xFF) << 22); }
if (matcher.groupCount() > 1)
res |= matcher.group(2) == null ? 0 : ((Integer.parseInt(matcher.group(2)) & 0xFF) << 14); return res;
if (matcher.groupCount() > 3) } catch (Exception e) {
res |= matcher.group(4) == null ? 0 : Integer.parseInt(matcher.group(4)) & 0x3FFF; return 0;
} }
}
return res;
} /**
catch (Exception e) { * Get mod version from integer. String will be in format %d.%d.%d
return 0; *
} * @param version - mod version in integer form.
} * @return {@link String} mod version.
*/
/** public static String convertModVersion(int version) {
* Get mod version from integer. String will be in format %d.%d.%d int a = (version >> 22) & 0xFF;
* int b = (version >> 14) & 0xFF;
* @param version - mod version in integer form. int c = version & 0x3FFF;
* @return {@link String} mod version. return String.format(Locale.ROOT, "%d.%d.%d", a, b, c);
*/ }
public static String convertModVersion(int version) {
int a = (version >> 22) & 0xFF; /**
int b = (version >> 14) & 0xFF; * {@code true} if the version v1 is larger than v2
int c = version & 0x3FFF; *
return String.format(Locale.ROOT, "%d.%d.%d", a, b, c); * @param v1 A Version string
} * @param v2 Another Version string
* @return v1 &gt; v2
/** */
* {@code true} if the version v1 is larger than v2 public static boolean isLargerVersion(String v1, String v2) {
* return convertModVersion(v1) > convertModVersion(v2);
* @param v1 A Version string }
* @param v2 Another Version string
* @return v1 &gt; v2 /**
*/ * {@code true} if the version v1 is larger or equal v2
public static boolean isLargerVersion(String v1, String v2) { *
return convertModVersion(v1) > convertModVersion(v2); * @param v1 A Version string
} * @param v2 Another Version string
* @return v1 &ge; v2
/** */
* {@code true} if the version v1 is larger or equal v2 public static boolean isLargerOrEqualVersion(String v1, String v2) {
* return convertModVersion(v1) >= convertModVersion(v2);
* @param v1 A Version string }
* @param v2 Another Version string
* @return v1 &ge; v2 private static void accept(Path file) {
*/ try {
public static boolean isLargerOrEqualVersion(String v1, String v2) { URI uri = URI.create("jar:" + file.toUri());
return convertModVersion(v1) >= convertModVersion(v2);
} FileSystem fs;
// boolean doClose = false;
private static void accept(Path file) { try {
try { fs = FileSystems.getFileSystem(uri);
URI uri = URI.create("jar:" + file.toUri()); } catch (Exception e) {
// doClose = true;
FileSystem fs; fs = FileSystems.newFileSystem(file);
// boolean doClose = false; }
try { if (fs != null) {
fs = FileSystems.getFileSystem(uri); try {
} Path modMetaFile = fs.getPath("fabric.mod.json");
catch (Exception e) { if (modMetaFile != null) {
// doClose = true; try (InputStream is = Files.newInputStream(modMetaFile)) {
fs = FileSystems.newFileSystem(file); //ModMetadata mc = ModMetadataParser.parseMetadata(is, uri.toString(), new LinkedList<String>());
} ModMetadata mc = readJSON(is, uri.toString());
if (fs != null) { if (mc != null) {
try { mods.put(mc.getId(), new ModInfo(mc, file));
Path modMetaFile = fs.getPath("fabric.mod.json"); }
if (modMetaFile != null) { }
try (InputStream is = Files.newInputStream(modMetaFile)) { }
//ModMetadata mc = ModMetadataParser.parseMetadata(is, uri.toString(), new LinkedList<String>()); } catch (Exception e) {
ModMetadata mc = readJSON(is, uri.toString()); BCLib.LOGGER.error("Error for " + uri + ": " + e.toString());
if (mc != null) { }
mods.put(mc.getId(), new ModInfo(mc, file)); //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 " + uri + ": " + e.toString()); }
}
//if (doClose) fs.close(); public static class ModInfo {
} public final ModMetadata metadata;
} public final Path jarPath;
catch (Exception e) {
BCLib.LOGGER.error("Error for " + file.toUri() + ": " + e.toString()); ModInfo(ModMetadata metadata, Path jarPath) {
e.printStackTrace(); this.metadata = metadata;
} this.jarPath = jarPath;
} }
public static class ModInfo { public static String versionToString(Version v) {
public final ModMetadata metadata; if (v instanceof SemanticVersion) {
public final Path jarPath; return versionToString((SemanticVersion) v);
}
ModInfo(ModMetadata metadata, Path jarPath) { return convertModVersion(convertModVersion(v.toString()));
this.metadata = metadata; }
this.jarPath = jarPath;
} public static String versionToString(SemanticVersion v) {
StringBuilder stringBuilder = new StringBuilder();
public static String versionToString(Version v) { boolean first = true;
if (v instanceof SemanticVersion) { final int cCount = Math.min(v.getVersionComponentCount(), 3);
return versionToString((SemanticVersion) v); for (int i = 0; i < cCount; i++) {
} if (first) {
return convertModVersion(convertModVersion(v.toString())); first = false;
} } else {
stringBuilder.append('.');
public static String versionToString(SemanticVersion v) { }
StringBuilder stringBuilder = new StringBuilder();
boolean first = true; stringBuilder.append(v.getVersionComponent(i));
final int cCount = Math.min(v.getVersionComponentCount(), 3); }
for (int i = 0; i < cCount; i++) {
if (first) { return stringBuilder.toString();
first = false; }
}
else { @Override
stringBuilder.append('.'); public String toString() {
} return "ModInfo{" + "id=" + metadata.getId() + ", version=" + metadata.getVersion() + ", jarPath=" + jarPath + '}';
}
stringBuilder.append(v.getVersionComponent(i));
} public String getVersion() {
if (metadata == null) {
return stringBuilder.toString(); return "0.0.0";
} }
return versionToString(metadata.getVersion());
@Override }
public String toString() { }
return "ModInfo{" + "id=" + metadata.getId() + ", version=" + metadata.getVersion() + ", jarPath=" + jarPath + '}';
}
public String getVersion() {
if (metadata == null) {
return "0.0.0";
}
return versionToString(metadata.getVersion());
}
}
} }