diff --git a/.github/workflows/build-and-test.yml b/.github/workflows/build-and-test.yml index e527f5c..08df9fe 100644 --- a/.github/workflows/build-and-test.yml +++ b/.github/workflows/build-and-test.yml @@ -6,12 +6,8 @@ name: Build and test on: pull_request: branches: [ master, main ] - paths-ignore: - - '**.md' push: branches: [ master, main ] - paths-ignore: - - '**.md' jobs: build-and-test: diff --git a/src/main/java/com/falsepattern/lib/FalsePatternLib.java b/src/main/java/com/falsepattern/lib/FalsePatternLib.java index 750ac58..c670956 100644 --- a/src/main/java/com/falsepattern/lib/FalsePatternLib.java +++ b/src/main/java/com/falsepattern/lib/FalsePatternLib.java @@ -1,5 +1,6 @@ package com.falsepattern.lib; +import com.falsepattern.lib.api.Version; import com.google.common.eventbus.EventBus; import com.google.common.eventbus.Subscribe; import cpw.mods.fml.common.DummyModContainer; @@ -7,6 +8,7 @@ import cpw.mods.fml.common.FMLCommonHandler; import cpw.mods.fml.common.LoadController; import cpw.mods.fml.common.ModMetadata; import cpw.mods.fml.common.event.FMLPreInitializationEvent; +import lombok.NonNull; import lombok.val; import lombok.var; import net.minecraft.launchwrapper.Launch; @@ -27,11 +29,13 @@ public class FalsePatternLib extends DummyModContainer { public static Logger libLog = LogManager.getLogger(ModInfo.MODNAME); public static final boolean developerEnvironment = (boolean) Launch.blackboard.get("fml.deobfuscatedEnvironment"); - private static Map loadedLibraries = new HashMap<>(); - private static Set mavenRepositories = new HashSet<>(); + private static final Map loadedLibraries = new HashMap<>(); + private static final Map loadedLibraryMods = new HashMap<>(); + private static final Set mavenRepositories = new HashSet<>(); private static boolean modWasDownloaded = false; + @SuppressWarnings("unchecked") public FalsePatternLib() { super(new ModMetadata()); libLog.info("FalsePatternLib has been awakened!"); @@ -68,18 +72,39 @@ public class FalsePatternLib extends DummyModContainer { mavenRepositories.add(url); } - public static void loadLibrary(String groupId, String artifactId, String version, String devSuffix, boolean isMod) { - libLog.info("Adding library {}:{}:{}", groupId, artifactId, version); + @SuppressWarnings("ResultOfMethodCallIgnored") + public static void loadLibrary(String loadingModId, String groupId, String artifactId, @NonNull Version minVersion, Version maxVersion, @NonNull Version preferredVersion, String devSuffix, boolean isMod) { + libLog.info("Adding library {}:{}:{}, requested by mod {}", groupId, artifactId, preferredVersion, loadingModId); var artifact = groupId + ":" + artifactId; if (loadedLibraries.containsKey(artifact)) { val currentVer = loadedLibraries.get(artifact); - if (!version.equals(currentVer)) { - libLog.warn("Tried to load library {}:{}:{}, but version {} was already loaded!", groupId, artifactId, version, currentVer); - return; + if (currentVer.equals(preferredVersion)) return; + val rangeString = "(minimum: " + minVersion + (maxVersion == null ? "" : ", maximum: " + maxVersion) + ")"; + if (minVersion.compareTo(currentVer) > 0 || (maxVersion != null && maxVersion.compareTo(currentVer) < 0)) { + for (int i = 0; i < 16; i++) { + libLog.fatal("ALERT VVVVVVVVVVVV ALERT"); + } + libLog.fatal("Library {}:{} already loaded with version {}, " + + "but a version in the range {} was requested! Thing may go horribly wrong! " + + "Requested by mod: {}, previously loaded by mod: {}", + groupId, artifactId, currentVer, + rangeString, + loadingModId, loadedLibraryMods.get(artifact)); + for (int i = 0; i < 16; i++) { + libLog.fatal("ALERT ^^^^^^^^^^^^ ALERT"); + } + } else { + libLog.info("Attempted loading of library {}:{} with preferred version {}, " + + "but version {} was already loaded, which matched the range {}. This is not an error. " + + "Requested by mod: {}, previously loaded by mod: {}", + groupId, artifactId, preferredVersion, + currentVer, rangeString, + loadingModId, loadedLibraryMods.get(artifact)); } + return; } val modsDir = new File(CoreLoadingPlugin.mcDir, "mods"); - val jarName = String.format("%s-%s%s.jar", artifactId, version, (developerEnvironment && devSuffix != null) ? ("-" + devSuffix) : ""); + val jarName = String.format("%s%s-%s%s.jar", isMod ? "" : (groupId + "-"), artifactId, preferredVersion, (developerEnvironment && devSuffix != null) ? ("-" + devSuffix) : ""); File file; if (isMod) { file = new File(modsDir, jarName); @@ -95,32 +120,33 @@ public class FalsePatternLib extends DummyModContainer { if (!isMod) { addToClasspath(file); } - loadedLibraries.put(artifact, version); - libLog.info("Library {}:{}:{} successfully loaded from disk!", groupId, artifactId, version); + loadedLibraries.put(artifact, preferredVersion); + libLog.info("Library {}:{}:{} successfully loaded from disk!", groupId, artifactId, preferredVersion); return; } catch (RuntimeException e) { - libLog.warn("Failed to load library {}:{}:{} from file! Redownloading...", groupId, artifactId, version); + libLog.warn("Failed to load library {}:{}:{} from file! Redownloading...", groupId, artifactId, preferredVersion); file.delete(); } } for (var repo: mavenRepositories) { try { if (!repo.endsWith("/")) repo = repo + "/"; - val url = new URL(String.format("%s%s/%s/%s/%s", repo, groupId.replace('.', '/'), artifactId, version, jarName)); + val url = new URL(String.format("%s%s/%s/%s/%s", repo, groupId.replace('.', '/'), artifactId, preferredVersion, jarName)); val connection = (HttpsURLConnection) url.openConnection(); connection.setConnectTimeout(1500); connection.setReadTimeout(1500); connection.setRequestProperty("User-Agent", "FalsePatternLib Downloader"); if (connection.getResponseCode() != 200) { - libLog.info("Artifact {}:{}:{} was not found on repo {}", groupId, artifactId, version, repo); + libLog.info("Artifact {}:{}:{} was not found on repo {}", groupId, artifactId, preferredVersion, repo); connection.disconnect(); continue; } - libLog.info("Downloading {}:{}:{} from {}", groupId, artifactId, version, repo); + libLog.info("Downloading {}:{}:{} from {}", groupId, artifactId, preferredVersion, repo); download(connection.getInputStream(), file); - libLog.info("Downloaded {}:{}:{}", groupId, artifactId, version); - loadedLibraries.put(artifact, version); + libLog.info("Downloaded {}:{}:{}", groupId, artifactId, preferredVersion); + loadedLibraries.put(artifact, preferredVersion); + loadedLibraryMods.put(artifact, loadingModId); if (isMod) { if (!modWasDownloaded) { modWasDownloaded = true; @@ -132,7 +158,9 @@ public class FalsePatternLib extends DummyModContainer { return; } catch (IOException ignored) {} } - throw new IllegalStateException("Failed to download library " + groupId + ":" + artifactId + ":" + version + " from any repository!"); + val errorMessage = "Failed to download library " + groupId + ":" + artifactId + ":" + preferredVersion + " from any repository! Requested by mod: " + loadingModId; + libLog.fatal(errorMessage); + throw new IllegalStateException(errorMessage); } private static void addToClasspath(File file) { diff --git a/src/main/java/com/falsepattern/lib/api/ComplexVersion.java b/src/main/java/com/falsepattern/lib/api/ComplexVersion.java new file mode 100644 index 0000000..f06dbc8 --- /dev/null +++ b/src/main/java/com/falsepattern/lib/api/ComplexVersion.java @@ -0,0 +1,50 @@ +package com.falsepattern.lib.api; + +import lombok.NonNull; +import lombok.val; + +import java.util.Arrays; +import java.util.stream.Collectors; + +public class ComplexVersion extends Version { + final Version[] versions; + public ComplexVersion(@NonNull Version mainVersion, Version... subVersions) { + this.versions = new Version[subVersions.length + 1]; + this.versions[0] = mainVersion; + System.arraycopy(subVersions, 0, this.versions, 1, subVersions.length); + } + + @Override + public int compareTo(@NonNull Version o) { + if (o instanceof ComplexVersion) { + val other = (ComplexVersion) o; + int count = Math.min(versions.length, other.versions.length); + for (int i = 0; i < count; i++) { + val result = versions[i].compareTo(other.versions[i]); + if (result != 0) return result; + } + if (versions.length != other.versions.length) { + return versions.length - other.versions.length; + } else { + return 0; + } + } else if (o instanceof SemanticVersion) { + val other = (SemanticVersion) o; + val result = other.compareTo(versions[0]); + if (result != 0) { + return result; + } + if (versions.length > 1) { + return -1; + } + return 0; + } else { + throw new IllegalArgumentException("Could not compare version with class " + o.getClass().getName()); + } + } + + @Override + public String toString() { + return Arrays.stream(versions).map(Version::toString).collect(Collectors.joining("-")); + } +} diff --git a/src/main/java/com/falsepattern/lib/api/DependencyLoader.java b/src/main/java/com/falsepattern/lib/api/DependencyLoader.java index 7da6156..312be98 100644 --- a/src/main/java/com/falsepattern/lib/api/DependencyLoader.java +++ b/src/main/java/com/falsepattern/lib/api/DependencyLoader.java @@ -1,10 +1,13 @@ package com.falsepattern.lib.api; import com.falsepattern.lib.FalsePatternLib; +import lombok.Builder; +import lombok.NonNull; public class DependencyLoader { - public static void loadLibrary(String groupId, String artifactId, String version, String devSuffix, boolean isMod) { - FalsePatternLib.loadLibrary(groupId, artifactId, version, devSuffix, isMod); + @Builder + public static void loadLibrary(String loadingModId, String groupId, String artifactId, @NonNull Version minVersion, Version maxVersion, @NonNull Version preferredVersion, String devSuffix, boolean isMod) { + FalsePatternLib.loadLibrary(loadingModId, groupId, artifactId, minVersion, maxVersion, preferredVersion, devSuffix, isMod); } public static void addMavenRepo(String url) { diff --git a/src/main/java/com/falsepattern/lib/api/SemanticVersion.java b/src/main/java/com/falsepattern/lib/api/SemanticVersion.java new file mode 100644 index 0000000..1e9bee4 --- /dev/null +++ b/src/main/java/com/falsepattern/lib/api/SemanticVersion.java @@ -0,0 +1,70 @@ +package com.falsepattern.lib.api; + +import lombok.Builder; +import lombok.Getter; +import lombok.NonNull; +import lombok.val; + +import java.util.Objects; + +public class SemanticVersion extends Version { + @Getter + private final int majorVersion; + @Getter + private final int minorVersion; + @Getter + private final int patchVersion; + @Getter + private final String preRelease; + @Getter + private final String build; + + @Builder + public SemanticVersion(int majorVersion, int minorVersion, int patchVersion, String preRelease, String build) { + this.majorVersion = majorVersion; + this.minorVersion = minorVersion; + this.patchVersion = patchVersion; + preRelease = preRelease.trim(); + build = build.trim(); + this.preRelease = "".equals(preRelease) ? null : preRelease; + this.build = "".equals(build) ? null : build; + } + + @Override + public int compareTo(@NonNull Version o) { + if (o instanceof ComplexVersion) { + val result = this.compareTo(((ComplexVersion)o).versions[0]); + if (result != 0) { + return result; + } else if (((ComplexVersion) o).versions.length > 1) { + return 1; + } else { + return 0; + } + } else if (o instanceof SemanticVersion) { + val other = (SemanticVersion)o; + if (majorVersion != other.majorVersion) { + return majorVersion - other.majorVersion; + } else if (minorVersion != other.minorVersion) { + return minorVersion - other.minorVersion; + } else if (patchVersion != other.patchVersion) { + return patchVersion - other.patchVersion; + } else if (!Objects.equals(preRelease, other.preRelease)) { + if (preRelease == null) { + return 1; + } else if (other.preRelease == null) { + return -1; + } + return preRelease.compareTo(other.preRelease); + } else { + return 0; + } + } + return 0; + } + + @Override + public String toString() { + return majorVersion + "." + minorVersion + "." + patchVersion + (preRelease == null ? "" : "-" + preRelease) + (build == null ? "" : "+" + build); + } +} diff --git a/src/main/java/com/falsepattern/lib/api/Version.java b/src/main/java/com/falsepattern/lib/api/Version.java new file mode 100644 index 0000000..42fee72 --- /dev/null +++ b/src/main/java/com/falsepattern/lib/api/Version.java @@ -0,0 +1,15 @@ +package com.falsepattern.lib.api; + +public abstract class Version implements Comparable { + Version(){} + + public boolean equals(Version other) { + return compareTo(other) == 0; + } + + @Override + public boolean equals(Object obj) { + if (!(obj instanceof Version)) return false; + return equals((Version) obj); + } +}