diff --git a/.gitignore b/.gitignore index 2edfc3f..a4db5de 100644 --- a/.gitignore +++ b/.gitignore @@ -149,3 +149,5 @@ gradle-app.setting .classpath # End of https://www.toptal.com/developers/gitignore/api/intellij+all,gradle,forgegradle,java + +srgmap.cfg \ No newline at end of file diff --git a/LICENSE b/LICENSE.md similarity index 100% rename from LICENSE rename to LICENSE.md diff --git a/README.MD b/README.MD index ea2e47b..b966466 100644 --- a/README.MD +++ b/README.MD @@ -1,17 +1,17 @@ A library for 1.7.10 with lots of useful stuff, licensed under the LGPL3.0+SNEED license. -| Package | Contents / Purpose | -|-----------------------------------------------------------------|-------------------------------------------------------------------------------------------| -| [asm](src/main/java/com/falsepattern/lib/asm) | ASM helper code | -| [compat](src/main/java/com/falsepattern/lib/compat) | Code backported from vanilla 1.12.2 | -| [config](src/main/java/com/falsepattern/lib/config) | 1.12.2-style annotation-based config library | -| [dependencies](src/main/java/com/falsepattern/lib/dependencies) | Runtime dependency loader which uses Maven | -| [mapping](src/main/java/com/falsepattern/lib/mapping) | Notch-SRG-MCP name mapping helper code | -| [mixin](src/main/java/com/falsepattern/lib/mixin) | Mixin loader plugin boilerplate code | -| [text](src/main/java/com/falsepattern/lib/text) | Better Chat and GUI text processing | -| [toasts](src/main/java/com/falsepattern/lib/toasts) | The toast system from newer versions, with some extras | -| [updates](src/main/java/com/falsepattern/lib/updates) | Helper code for update checking | -| [util](src/main/java/com/falsepattern/lib/util) | Additional utilities that do not fit the above categories, see below for more information | +| Package | Contents / Purpose | +|-----------------------------------------------------------------|-----------------------------------------------------------------------------------------------------------------------------------------| +| [asm](src/main/java/com/falsepattern/lib/asm) | ASM helper code | +| [compat](src/main/java/com/falsepattern/lib/compat) | Code backported from vanilla 1.12.2 | +| [config](src/main/java/com/falsepattern/lib/config) | 1.12.2-style annotation-based config library | +| [dependencies](src/main/java/com/falsepattern/lib/dependencies) | Runtime dependency loader which uses Maven (DEPRECATED, see [The new data-driven dependency format](src/main/resources/DEPENDENCIES.MD) | +| [mapping](src/main/java/com/falsepattern/lib/mapping) | Notch-SRG-MCP name mapping helper code | +| [mixin](src/main/java/com/falsepattern/lib/mixin) | Mixin loader plugin boilerplate code | +| [text](src/main/java/com/falsepattern/lib/text) | Better Chat and GUI text processing | +| [toasts](src/main/java/com/falsepattern/lib/toasts) | The toast system from newer versions, with some extras | +| [updates](src/main/java/com/falsepattern/lib/updates) | Helper code for update checking | +| [util](src/main/java/com/falsepattern/lib/util) | Additional utilities that do not fit the above categories, see below for more information | The contents of the [util](src/main/java/com/falsepattern/lib/util) package so far: @@ -28,6 +28,4 @@ See the [@StableAPI](src/main/java/com/falsepattern/lib/StableAPI.java) annotati API stability. The update checker module and the library downloader module can be toggled with the `ENABLE_UPDATE_CHECKER` and -`ENABLE_LIBRARY_DOWNLOADS` booleans in the `falsepatternlib.cfg` config file respectively. Note that the update checker -depends on the module downloader to grab [FJson](https://github.com/FalsePattern/FJson), so if it's not already present -on disk, disabling the library loader breaks the update checker. This is only required the first time download, though. +`ENABLE_LIBRARY_DOWNLOADS` booleans in the `falsepatternlib.cfg` config file respectively. diff --git a/build.gradle b/build.gradle index 74849c5..f78365b 100644 --- a/build.gradle +++ b/build.gradle @@ -1,4 +1,4 @@ -//version: 1656003793falsepattern59 +//version: 1656003793falsepattern60 /* DO NOT CHANGE THIS FILE! @@ -282,9 +282,13 @@ if(file("addon.gradle").exists()) { apply from: 'repositories.gradle' configurations { - implementation.extendsFrom(shadowImplementation) // TODO: remove after all uses are refactored - implementation.extendsFrom(shadowCompile) - implementation.extendsFrom(shadeCompile) + for (config in [shadowImplementation, shadowCompile, shadeCompile]) { + implementation.extendsFrom(shadeCompile) + compileClasspath.extendsFrom(config) + runtimeClasspath.extendsFrom(config) + testCompileClasspath.extendsFrom(config) + testRuntimeClasspath.extendsFrom(config) + } } repositories { @@ -314,6 +318,8 @@ dependencies { compile("org.spongepowered:mixin:0.8.5-gasstation_7") compile("com.llamalad7:MixinExtras:0.1.1-gasstation") compile("com.falsepattern:00gasstation-mc1.7.10:0.5.1:dev") + runtimeClasspath('org.jetbrains:intellij-fernflower:1.2.1.16') + testRuntimeClasspath('org.jetbrains:intellij-fernflower:1.2.1.16') } else if(hasMixinDeps.toBoolean()) { runtime("com.llamalad7:MixinExtras:0.1.1-gasstation") runtime("org.spongepowered:mixin:0.8.5-gasstation_7") @@ -668,8 +674,11 @@ publish.dependsOn(build) publishing { publications { maven(MavenPublication) { + from components.java artifact source: usesShadowedDependencies.toBoolean() ? shadowJar : jar, classifier: "" - artifact source: sourcesJar, classifier: "sources" + if (!project.hasProperty("noPublishedSources") || !noPublishedSources) { + artifact source: sourcesJar, classifier: "sources" + } artifact source: usesShadowedDependencies.toBoolean() ? shadowDevJar : devJar, classifier: "dev" if (apiPackage) { artifact source: apiJar, classifier: "api" diff --git a/dependencies.gradle b/dependencies.gradle index 7286317..1f2c034 100644 --- a/dependencies.gradle +++ b/dependencies.gradle @@ -1,12 +1,14 @@ dependencies { + //Dev-only deps compileOnly("org.projectlombok:lombok:1.18.24") { transitive = false } - compileOnly("org.spongepowered:mixin:0.8.3-gasstation") - compileOnly("com.llamalad7:MixinExtras:0.0.12-gasstation") - compileOnly("com.falsepattern:00gasstation-mc1.7.10:0.2.1:dev") - compileOnly("com.falsepattern:json:0.4.1") - compileOnly("org.tukaani:xz:1.9") - testCompile("org.tukaani:xz:1.9") - annotationProcessor("org.projectlombok:lombok:1.18.22") + annotationProcessor("org.projectlombok:lombok:1.18.24") + + compileOnly("org.jetbrains:annotations:23.1.0") + + //Mod deps + compileOnly("org.spongepowered:mixin:0.8.5-gasstation_7") + compileOnly("com.llamalad7:MixinExtras:0.1.1-gasstation") + compileOnly("com.falsepattern:00gasstation-mc1.7.10:0.5.1:dev") } diff --git a/gradle.properties b/gradle.properties index 09a56bd..2ad5b02 100644 --- a/gradle.properties +++ b/gradle.properties @@ -187,7 +187,7 @@ containsMixinsAndOrCoreModOnly = false # If enabled, you may use 'shadowCompile' for dependencies. They will be integrated in your jar. It is your # responsibility check the licence and request permission for distribution, if required. -usesShadowedDependencies = false +usesShadowedDependencies = true # If enabled, class stubbing will be enabled. In this mode, all classes with a package named "stubpackage" in their # path will be removed, and all classes that refer to said classes will be modified so that the "stubpackage" will map diff --git a/repositories.gradle b/repositories.gradle index 3d5cf5e..d6151d0 100644 --- a/repositories.gradle +++ b/repositories.gradle @@ -11,4 +11,8 @@ repositories { name = "mvnpattern" url = "https://mvn.falsepattern.com/releases/" } + maven { + name = "repsy_ventooth" + url = "https://repo.repsy.io/mvn/ventooth/public/" + } } diff --git a/src/main/java/com/falsepattern/lib/DeprecationDetails.java b/src/main/java/com/falsepattern/lib/DeprecationDetails.java index 8ff8c82..32589d4 100644 --- a/src/main/java/com/falsepattern/lib/DeprecationDetails.java +++ b/src/main/java/com/falsepattern/lib/DeprecationDetails.java @@ -34,4 +34,5 @@ import java.lang.annotation.RetentionPolicy; @StableAPI(since = "0.10.0") public @interface DeprecationDetails { @StableAPI.Expose String deprecatedSince(); + @StableAPI.Expose(since = "0.11.0") String replacement() default ""; } diff --git a/src/main/java/com/falsepattern/lib/dependencies/ComplexVersion.java b/src/main/java/com/falsepattern/lib/dependencies/ComplexVersion.java index 59bd9cb..a6f0737 100644 --- a/src/main/java/com/falsepattern/lib/dependencies/ComplexVersion.java +++ b/src/main/java/com/falsepattern/lib/dependencies/ComplexVersion.java @@ -56,7 +56,7 @@ public class ComplexVersion extends Version { } } else if (o instanceof SemanticVersion) { val other = (SemanticVersion) o; - val result = other.compareTo(versions[0]); + val result = versions[0].compareTo(other); if (result != 0) { return result; } diff --git a/src/main/java/com/falsepattern/lib/dependencies/DependencyLoader.java b/src/main/java/com/falsepattern/lib/dependencies/DependencyLoader.java index ed3e50c..40f9d16 100644 --- a/src/main/java/com/falsepattern/lib/dependencies/DependencyLoader.java +++ b/src/main/java/com/falsepattern/lib/dependencies/DependencyLoader.java @@ -28,6 +28,13 @@ import lombok.NonNull; import java.util.concurrent.CompletableFuture; +/** + * DEPRECATED. See the DEPENDENCIES.MD files in the root of the resources for more information. + *

+ * This class will stay here for legacy compatibility + */ +@Deprecated +@DeprecationDetails(deprecatedSince = "0.11.0") @StableAPI(since = "0.6.0") public class DependencyLoader { @@ -64,8 +71,9 @@ public class DependencyLoader { return new VoidBuilder(); } - @StableAPI(since = "0.6.0") + @Deprecated @DeprecationDetails(deprecatedSince = "0.10.0") + @StableAPI(since = "0.6.0") public static class VoidBuilder { private String loadingModId; private String groupId; diff --git a/src/main/java/com/falsepattern/lib/dependencies/Library.java b/src/main/java/com/falsepattern/lib/dependencies/Library.java index 89f6b21..3fbe3fa 100644 --- a/src/main/java/com/falsepattern/lib/dependencies/Library.java +++ b/src/main/java/com/falsepattern/lib/dependencies/Library.java @@ -21,9 +21,12 @@ package com.falsepattern.lib.dependencies; +import com.falsepattern.lib.DeprecationDetails; import com.falsepattern.lib.StableAPI; import lombok.NonNull; +@Deprecated +@DeprecationDetails(deprecatedSince = "0.11.0") @StableAPI(since = "0.10.0") public class Library { @StableAPI.Expose diff --git a/src/main/java/com/falsepattern/lib/dependencies/RawVersion.java b/src/main/java/com/falsepattern/lib/dependencies/RawVersion.java new file mode 100644 index 0000000..e3447f5 --- /dev/null +++ b/src/main/java/com/falsepattern/lib/dependencies/RawVersion.java @@ -0,0 +1,43 @@ +/* + * Copyright (C) 2022 FalsePattern + * All Rights Reserved + * + * The above copyright notice, this permission notice and the word "SNEED" + * shall be included in all copies or substantial portions of the Software. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + */ + +package com.falsepattern.lib.dependencies; + +import org.jetbrains.annotations.NotNull; + +public class RawVersion extends Version { + public final String versionString; + public RawVersion(String versionString) { + this.versionString = versionString; + } + @Override + public int compareTo(@NotNull Version o) { + if (o instanceof RawVersion) { + return versionString.compareTo(((RawVersion) o).versionString); + } else if (o instanceof SemanticVersion) { + return 1; + } else if (o instanceof ComplexVersion) { + return 1; + } else { + throw new IllegalArgumentException("Unknown version type: " + o.getClass().getName()); + } + } +} diff --git a/src/main/java/com/falsepattern/lib/internal/Internet.java b/src/main/java/com/falsepattern/lib/internal/Internet.java index 008ee36..86a3fdb 100644 --- a/src/main/java/com/falsepattern/lib/internal/Internet.java +++ b/src/main/java/com/falsepattern/lib/internal/Internet.java @@ -20,17 +20,21 @@ */ package com.falsepattern.lib.internal; +import lombok.Cleanup; import lombok.val; import lombok.var; import java.io.ByteArrayOutputStream; +import java.io.File; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.net.HttpURLConnection; import java.net.URL; +import java.nio.file.Files; import java.util.concurrent.CompletableFuture; import java.util.concurrent.CompletionException; +import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicReference; import java.util.function.Consumer; @@ -53,18 +57,43 @@ public class Internet { } } - public static CompletableFuture download(URL URL) { + public static CompletableFuture download(URL... urls) { return CompletableFuture.supplyAsync(() -> { val result = new ByteArrayOutputStream(); - AtomicReference caught = new AtomicReference<>(); - connect(URL, caught::set, (input) -> { - try { - transferAndClose(input, result); - } catch (IOException e) { - throw new CompletionException(e); - } - }); - return result.toByteArray(); + val caught = new AtomicReference(); + val success = new AtomicBoolean(false); + for (val url : urls) { + connect(url, caught::set, (input) -> { + try { + transferAndClose(input, result); + success.set(true); + } catch (IOException e) { + throw new CompletionException(e); + } + }); + if (success.get()) return result.toByteArray(); + } + throw new CompletionException(caught.get()); + }); + } + + public static CompletableFuture downloadFile(File file, URL... urls) { + return CompletableFuture.runAsync(() -> { + val caught = new AtomicReference(); + val success = new AtomicBoolean(false); + for (val url : urls) { + connect(url, caught::set, (input) -> { + try { + @Cleanup val outputStream = Files.newOutputStream(file.toPath()); + transferAndClose(input, outputStream); + success.set(true); + } catch (IOException e) { + throw new CompletionException(e); + } + }); + if (success.get()) return; + } + throw new CompletionException(caught.get()); }); } diff --git a/src/main/java/com/falsepattern/lib/internal/asm/CoreLoadingPlugin.java b/src/main/java/com/falsepattern/lib/internal/asm/CoreLoadingPlugin.java index 3baded9..db2475f 100644 --- a/src/main/java/com/falsepattern/lib/internal/asm/CoreLoadingPlugin.java +++ b/src/main/java/com/falsepattern/lib/internal/asm/CoreLoadingPlugin.java @@ -21,6 +21,7 @@ package com.falsepattern.lib.internal.asm; import com.falsepattern.lib.internal.Tags; +import com.falsepattern.lib.internal.impl.dependencies.DependencyLoaderImpl; import com.falsepattern.lib.mapping.MappingManager; import lombok.Getter; import lombok.val; @@ -51,6 +52,8 @@ public class CoreLoadingPlugin implements IFMLLoadingPlugin { throw skillIssue("Thermos is not supported by FalsePatternLib, please use a normal forge server."); } catch (ClassNotFoundException ignored) { } + //Scan for dependencies now + DependencyLoaderImpl.scanDeps(); //Initializing the rest MappingManager.initialize(); } diff --git a/src/main/java/com/falsepattern/lib/internal/impl/dependencies/DepRoot.java b/src/main/java/com/falsepattern/lib/internal/impl/dependencies/DepRoot.java new file mode 100644 index 0000000..a180451 --- /dev/null +++ b/src/main/java/com/falsepattern/lib/internal/impl/dependencies/DepRoot.java @@ -0,0 +1,69 @@ +/* + * Copyright (C) 2022 FalsePattern + * All Rights Reserved + * + * The above copyright notice, this permission notice and the word "SNEED" + * shall be included in all copies or substantial portions of the Software. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + */ + +package com.falsepattern.lib.internal.impl.dependencies; + +import com.falsepattern.lib.StableAPI; +import com.google.gson.annotations.Expose; +import lombok.Data; +import lombok.experimental.Accessors; + +import java.util.List; + +@Data +@Accessors(fluent = true) +public class DepRoot { + private String source; + @Expose + @StableAPI.Expose(since = "__INTERNAL__") + private List repositories; + @Expose + @StableAPI.Expose(since = "__INTERNAL__") + private Dependencies dependencies; + + @Data + @Accessors(fluent = true) + public static class Dependencies { + @Expose + @StableAPI.Expose(since = "__INTERNAL__") + private SidedDependencies always; + @Expose + @StableAPI.Expose(since = "__INTERNAL__") + private SidedDependencies obf; + @Expose + @StableAPI.Expose(since = "__INTERNAL__") + private SidedDependencies dev; + } + + @Data + @Accessors(fluent = true) + public static class SidedDependencies { + @Expose + @StableAPI.Expose(since = "__INTERNAL__") + private List common; + @Expose + @StableAPI.Expose(since = "__INTERNAL__") + private List client; + @Expose + @StableAPI.Expose(since = "__INTERNAL__") + private List server; + } +} diff --git a/src/main/java/com/falsepattern/lib/internal/impl/dependencies/DependencyLoaderImpl.java b/src/main/java/com/falsepattern/lib/internal/impl/dependencies/DependencyLoaderImpl.java index f6529f0..186ecf2 100644 --- a/src/main/java/com/falsepattern/lib/internal/impl/dependencies/DependencyLoaderImpl.java +++ b/src/main/java/com/falsepattern/lib/internal/impl/dependencies/DependencyLoaderImpl.java @@ -20,14 +20,18 @@ */ package com.falsepattern.lib.internal.impl.dependencies; -import com.falsepattern.lib.dependencies.DependencyLoader; +import com.falsepattern.lib.DeprecationDetails; import com.falsepattern.lib.dependencies.Library; +import com.falsepattern.lib.dependencies.RawVersion; +import com.falsepattern.lib.dependencies.SemanticVersion; import com.falsepattern.lib.dependencies.Version; import com.falsepattern.lib.internal.Internet; import com.falsepattern.lib.internal.Share; import com.falsepattern.lib.internal.Tags; import com.falsepattern.lib.internal.config.LibraryConfig; import com.falsepattern.lib.util.FileUtil; +import com.google.gson.GsonBuilder; +import com.google.gson.JsonParser; import io.netty.util.internal.ConcurrentSet; import lombok.NonNull; import lombok.RequiredArgsConstructor; @@ -37,19 +41,27 @@ import lombok.var; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; +import net.minecraft.launchwrapper.Launch; import net.minecraft.launchwrapper.LaunchClassLoader; +import cpw.mods.fml.relauncher.FMLLaunchHandler; + import java.io.BufferedOutputStream; import java.io.File; import java.io.IOException; import java.io.InputStream; +import java.io.InputStreamReader; +import java.net.MalformedURLException; import java.net.URL; import java.nio.file.Files; import java.nio.file.Paths; import java.nio.file.StandardCopyOption; import java.security.MessageDigest; import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashMap; import java.util.Map; +import java.util.Objects; import java.util.Set; import java.util.concurrent.CompletableFuture; import java.util.concurrent.ConcurrentHashMap; @@ -57,6 +69,12 @@ import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicLong; +import java.util.function.Function; +import java.util.jar.JarFile; +import java.util.regex.Pattern; +import java.util.stream.Collectors; +import java.util.stream.Stream; +import java.util.zip.ZipFile; public class DependencyLoaderImpl { @@ -67,7 +85,7 @@ public class DependencyLoaderImpl { private static final Map loadedLibraries = new ConcurrentHashMap<>(); private static final Map loadedLibraryMods = new ConcurrentHashMap<>(); private static final Set mavenRepositories = new ConcurrentSet<>(); - private static final Logger log = LogManager.getLogger(Tags.MODNAME + " Library Downloader"); + private static final Logger LOG = LogManager.getLogger(Tags.MODNAME + " Library Loader"); private static final AtomicLong counter = new AtomicLong(0); private static final ExecutorService executor = Executors.newCachedThreadPool(r -> { @@ -126,16 +144,16 @@ public class DependencyLoaderImpl { private static void checkedDelete(File file) { if (!file.delete()) { - log.fatal("Failed to delete file {}", file); + LOG.fatal("Failed to delete file {}", file); throw new RuntimeException("Failed to delete file " + file); } } private static synchronized void addToClasspath(File file) { try { - val cl = (LaunchClassLoader) DependencyLoader.class.getClassLoader(); + val cl = (LaunchClassLoader) DependencyLoaderImpl.class.getClassLoader(); cl.addURL(file.toURI().toURL()); - log.debug("Injected file {} into classpath!", file.getPath()); + LOG.debug("Injected file {} into classpath!", file.getPath()); } catch (Exception e) { throw new RuntimeException("Failed to add library to classpath: " + file.getAbsolutePath(), e); } @@ -149,10 +167,14 @@ public class DependencyLoaderImpl { Internet.transferAndClose(is, new BufferedOutputStream(Files.newOutputStream(target.toPath()))); } + @Deprecated + @DeprecationDetails(deprecatedSince = "0.11.0") public static void loadLibraries(Library... libraries) { loadLibrariesAsync(libraries).join(); } + @Deprecated + @DeprecationDetails(deprecatedSince = "0.11.0") public static CompletableFuture loadLibrariesAsync(Library... libraries) { val futures = new ArrayList>(); for (val library : libraries) { @@ -164,6 +186,206 @@ public class DependencyLoaderImpl { return CompletableFuture.allOf(futures.toArray(new CompletableFuture[0])); } + private static Stream scanSourceMetaInf(URL source) { + if (!source.getProtocol().equals("file")) { + LOG.warn("Skipping non-file source: {}", source); + return Stream.empty(); + } + LOG.debug("Scanning {} for dependencies", source); + val path = source.getPath(); + val output = new ArrayList(); + if (path.endsWith(".jar") || path.endsWith(".zip")) { + //Scan jar file for json in META-INF, add them to the list + try (val jarFile = (ZipFile)(path.endsWith("jar") ? new JarFile(path) : new ZipFile(path))) { + val entries = jarFile.entries(); + while (entries.hasMoreElements()) { + val entry = entries.nextElement(); + if (!entry.getName().startsWith("META-INF") || !entry.getName().endsWith(".json")) { + continue; + } + try { + output.add(new URL("jar:" + source + "!/" + entry.getName())); + } catch (MalformedURLException e) { + LOG.error("Failed to add json source {} to dependency source list: {}", entry.getName(), e); + } + } + } catch (IOException e) { + LOG.error("Failed to open jar file {}", path); + } + } else { + val dir = new File(path); + if (!dir.exists() || !dir.isDirectory()) { + LOG.warn("Skipping non-directory, nor jar, nor zip source: {}", source); + return Stream.empty(); + } + //Scan directory for json in META-INF, add them to the list + val metaInf = new File(dir, "META-INF"); + if (!metaInf.exists() || !metaInf.isDirectory()) { + return Stream.empty(); + } + val files = metaInf.listFiles(); + if (files == null) { + return Stream.empty(); + } + for (val file : files) { + if (!file.getName().endsWith(".json")) { + continue; + } + try { + output.add(file.toURI().toURL()); + } catch (MalformedURLException e) { + LOG.error("Failed to add json source {} to dependency source list: {}", file.getName(), e); + } + } + } + return output.stream(); + } + + private static Stream grabSourceCandidatesFromFolder(File folder) { + if (!folder.exists() || !folder.isDirectory()) { + LOG.warn("File does not exist or is not a directory: {}", folder); + } else { + val files = folder.listFiles(); + if (files == null) { + LOG.warn("Folder is not readable: {}", folder); + } else { + return Arrays.stream(files).map(file -> { + try { + LOG.trace("Adding {} to dependency source list", file); + return file.toURI().toURL(); + } catch (MalformedURLException e) { + LOG.error("Failed to add mod file {} to dependency source list: {}", file.getName(), e); + return null; + } + }).filter(Objects::nonNull); + } + } + return Stream.empty(); + } + + private static Stream> flatMap(Pair pair, Function> mapper) { + val a = pair.a; + return mapper.apply(pair.b).map(b -> new Pair<>(a, b)); + } + + private static final Pattern VERSION_PATTERN = Pattern.compile("" + + "(0|[1-9]\\d*)(?:\\.(0|[1-9]\\d*))?(?:\\.(0|[1-9]\\d*))?" + + "(?:-((?:(?:[0-9]+[a-zA-Z-][\\w-]*)|(?:[a-zA-Z][\\w-]*)|(?:[1-9]\\d*)|0)" + + "(?:\\.(?:(?:[0-9]+[a-zA-Z-][\\w-]*)|(?:[a-zA-Z][\\w-]*)|(?:[1-9]\\d*)|0))*))?" + + "(?:\\+([\\w-]+(\\.[\\w-]+)*))?"); + public static void scanDeps() { + LOG.debug("Discovering dependency source candidates..."); + val modsDir = new File(FileUtil.getMinecraftHome(), "mods"); + val mods1710Dir = new File(modsDir, "1.7.10"); + val dependencySpecs = Stream.of(Launch.classLoader.getSources().stream(), + grabSourceCandidatesFromFolder(modsDir), + grabSourceCandidatesFromFolder(mods1710Dir)) + .parallel() + .flatMap((i) -> i) + .flatMap(DependencyLoaderImpl::scanSourceMetaInf) + .map((source) -> { + //Convert source to GSON json + try (val is = source.openStream()) { + val json = new JsonParser().parse(new InputStreamReader(is)).getAsJsonObject(); + if (!(json.isJsonObject() && + json.has("identifier") && + json.get("identifier") + .getAsString() + .equals("falsepatternlib_dependencies") + )) { + return null; + } + val builder = new GsonBuilder(); + builder.excludeFieldsWithoutExposeAnnotation(); + val gson = builder.create(); + json.remove("identifier"); + val root = gson.fromJson(json, DepRoot.class); + root.source(source.toString()); + return root; + } catch (IOException e) { + LOG.error("Failed to read json from source {}: {}", source, e); + return null; + } + }) + .filter(Objects::nonNull) + .collect(Collectors.toSet()); + mavenRepositories.addAll(dependencySpecs.stream() + .flatMap((dep) -> dep.repositories().stream()) + .collect(Collectors.toSet())); + val artifacts = dependencySpecs.parallelStream() + .map((root) -> new Pair<>(root.source(), root.dependencies())) + .flatMap(pair -> flatMap(pair, (dep) -> Stream.of(dep.always(), Share.DEV_ENV ? dep.dev() : dep.obf()).parallel())) + .flatMap(pair -> flatMap(pair, (dep) -> Stream.concat(dep.common().parallelStream(), + FMLLaunchHandler.side().isClient() ? + dep.client().parallelStream() : + dep.server().parallelStream()).parallel())) + .map((pair) -> { + val source = pair.a; + val dep = pair.b; + val parts = dep.split(":"); + if (parts.length < 3) { + LOG.error("Invalid dependency: {}", dep); + return null; + } + val groupId = parts[0]; + val artifactId = parts[1]; + Version version; + try { + val matcher = VERSION_PATTERN.matcher(parts[2]); + if (!matcher.matches()) { + throw new IllegalArgumentException("Invalid version: " + parts[2]); + } + val major = Integer.parseInt(matcher.group(1)); + val minor = matcher.group(2) == null ? -1 : Integer.parseInt(matcher.group(2)); + val patch = matcher.group(3) == null ? -1 : Integer.parseInt(matcher.group(3)); + val preRelease = matcher.group(4); + val build = matcher.group(5); + version = new SemanticVersion(major, minor, patch, preRelease, build); + } catch (IllegalArgumentException e) { + LOG.warn("Unparseable dependency version {}:{}:{} from {}", groupId, artifactId, parts[2], source); + version = new RawVersion(parts[2]); + } + final String classifier = parts.length > 3 ? parts[3] : null; + if (classifier != null) { + LOG.info("Found dependency: {}:{}:{}:{} from {}", groupId, artifactId, + version, classifier, source); + } else { + LOG.info("Found dependency: {}:{}:{} from {}", groupId, artifactId, + version, source); + } + return new DependencyLoadTask(source, groupId, artifactId, + version, null, version, + classifier, classifier); + }) + .filter(Objects::nonNull) + .collect(Collectors.toSet()); + val artifactMap = new HashMap(); + for (val artifact: artifacts) { + val id = artifact.getGroupArtifact(); + if (artifactMap.containsKey(id)) { + val otherArtifact = artifactMap.get(id); + //TODO: Check for conflicts + if (artifact.preferredVersion.compareTo(otherArtifact.preferredVersion) > 0) { + LOG.info("Replacing dependency {}:{} from {} with version {} from {}", + otherArtifact.getGroupArtifact(), otherArtifact.preferredVersion, + otherArtifact.loadingModId, + artifact.preferredVersion, + artifact.loadingModId); + artifactMap.put(id, artifact); + } + } else { + artifactMap.put(id, artifact); + } + } + + val futures = new ArrayList>(); + for (val task : artifactMap.values()) { + futures.add(CompletableFuture.runAsync(task::load, executor)); + } + val theFuture = CompletableFuture.allOf(futures.toArray(new CompletableFuture[0])); + theFuture.join(); + } + @RequiredArgsConstructor private static class DependencyLoadTask { @NonNull @@ -189,29 +411,33 @@ public class DependencyLoaderImpl { private File file; private void load() { - setupLibraryNames(); - if (loadedLibraries.containsKey(artifact)) { - alreadyLoaded(); - return; - } - setupPaths(); - if (tryLoadingExistingFile()) { - return; - } - validateDownloadsAllowed(); - for (var repo : mavenRepositories) { - if (tryDownloadFromMaven(repo)) { + try { + setupLibraryNames(); + if (loadedLibraries.containsKey(artifact)) { + alreadyLoaded(); return; } + setupPaths(); + if (tryLoadingExistingFile()) { + return; + } + validateDownloadsAllowed(); + for (var repo : mavenRepositories) { + if (tryDownloadFromMaven(repo)) { + return; + } + } + crashCouldNotDownload(); + } catch (Exception e) { + LOG.fatal(e); } - crashCouldNotDownload(); } private void crashCouldNotDownload() { val errorMessage = "Failed to download library " + groupId + ":" + artifactId + ":" + preferredVersion + ((suffix != null) ? ":" + suffix : "") + " from any repository! Requested by mod: " + loadingModId; - log.fatal(errorMessage); + LOG.fatal(errorMessage); throw new IllegalStateException(errorMessage); } @@ -219,7 +445,7 @@ public class DependencyLoaderImpl { suffix = Share.DEV_ENV ? devSuffix : regularSuffix; artifactLogName = String.format("%s:%s:%s%s", groupId, artifactId, preferredVersion, suffix != null ? "-" + suffix : ""); - log.info("Adding library {}, requested by mod {}", artifactLogName, loadingModId); + LOG.info("Adding library {}, requested by mod {}", artifactLogName, loadingModId); artifact = groupId + ":" + artifactId + ":" + suffix; } @@ -231,18 +457,18 @@ public class DependencyLoaderImpl { 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++) { - log.fatal("ALERT VVVVVVVVVVVV ALERT"); + LOG.fatal("ALERT VVVVVVVVVVVV ALERT"); } - log.fatal("Library {}:{}{} already loaded with version {}, " + + LOG.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, suffix != null ? ":" + suffix : "", currentVer, rangeString, loadingModId, loadedLibraryMods.get(artifact)); for (int i = 0; i < 16; i++) { - log.fatal("ALERT ^^^^^^^^^^^^ ALERT"); + LOG.fatal("ALERT ^^^^^^^^^^^^ ALERT"); } } else { - log.info("Attempted loading of library {}:{}{} with preferred version {}, " + + LOG.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, suffix != null ? ":" + suffix : "", preferredVersion, currentVer, rangeString, loadingModId, @@ -266,23 +492,23 @@ public class DependencyLoaderImpl { jarName = groupId + "-" + mavenJarName; if (!libDir.exists()) { if (!libDir.mkdirs()) { - log.fatal("Failed to create directory {}", libDir); + LOG.fatal("Failed to create directory {}", libDir); throw new RuntimeException("Failed to create directory " + libDir); } } if (oldLibDir.exists()) { - log.info("Migrating old library folder. From: " + oldLibDir.getAbsolutePath() + ", To: " + libDir.getAbsolutePath()); + LOG.info("Migrating old library folder. From: " + oldLibDir.getAbsolutePath() + ", To: " + libDir.getAbsolutePath()); val oldFiles = oldLibDir.listFiles(); if (oldFiles != null) { for (val file: oldFiles) { try { Files.move(file.toPath(), libDir.toPath().resolve(oldLibDir.toPath().relativize(file.toPath())), StandardCopyOption.REPLACE_EXISTING); } catch (IOException e) { - log.warn("Failed to move file " + file.getName() + " to new dir! Deleting instead."); + LOG.warn("Failed to move file " + file.getName() + " to new dir! Deleting instead."); try { Files.deleteIfExists(file.toPath()); } catch (IOException ex) { - log.warn("Failed to delete file " + file.getPath() + "!"); + LOG.warn("Failed to delete file " + file.getPath() + "!"); file.deleteOnExit(); } } @@ -291,7 +517,7 @@ public class DependencyLoaderImpl { try { Files.deleteIfExists(oldLibDir.toPath()); } catch (IOException e) { - log.warn("Failed to delete old library directory!"); + LOG.warn("Failed to delete old library directory!"); oldLibDir.deleteOnExit(); } } @@ -307,21 +533,21 @@ public class DependencyLoaderImpl { if (status == ChecksumStatus.FAILED) { return false; } else if (status == ChecksumStatus.MISSING) { - log.debug("Library {} is missing checksum data! Either it was manually deleted, " + + LOG.debug("Library {} is missing checksum data! Either it was manually deleted, " + "or the source repo didn't have it in the first place", artifactLogName); } } catch (IOException e) { - log.error("Failed to execute validation check for " + artifactLogName, e); + LOG.error("Failed to execute validation check for " + artifactLogName, e); checkedDelete(file); return false; } try { addToClasspath(file); loadedLibraries.put(artifact, preferredVersion); - log.debug("Library {} successfully loaded from disk!", artifactLogName); + LOG.debug("Library {} successfully loaded from disk!", artifactLogName); return true; } catch (RuntimeException e) { - log.warn("Failed to load library {} from file! Re-downloading...", artifactLogName); + LOG.warn("Failed to load library {} from file! Re-downloading...", artifactLogName); checkedDelete(file); return false; } @@ -333,7 +559,7 @@ public class DependencyLoaderImpl { ((suffix != null) ? ":" + suffix : "") + ": " + Tags.MODNAME + " library downloading has been disabled in the config, and the library is not present " + "on disk! Requested by mod: " + loadingModId; - log.fatal(errorMessage); + LOG.fatal(errorMessage); throw new IllegalStateException(errorMessage); } } @@ -354,15 +580,15 @@ public class DependencyLoaderImpl { } val success = new AtomicBoolean(false); Internet.connect(new URL(url), - (ex) -> log.debug("Artifact {} could not be downloaded from repo {}: {}", + (ex) -> LOG.debug("Artifact {} could not be downloaded from repo {}: {}", artifactLogName, finalRepo, ex.getMessage()), (input) -> { - log.debug("Downloading {} from {}", artifactLogName, finalRepo); + LOG.debug("Downloading {} from {}", artifactLogName, finalRepo); download(input, file); - log.debug("Downloaded {} from {}", artifactLogName, finalRepo); + LOG.debug("Downloaded {} from {}", artifactLogName, finalRepo); success.set(true); }); if (success.get()) { - log.debug("Validating checksum for {}", artifactLogName); + LOG.debug("Validating checksum for {}", artifactLogName); val hadChecksum = validateChecksum(url); switch (hadChecksum) { case FAILED: @@ -370,7 +596,7 @@ public class DependencyLoaderImpl { case OK: break; case MISSING: - log.warn("The library {} had no checksum available on the repository.\n" + + LOG.warn("The library {} had no checksum available on the repository.\n" + "There's a chance it might have gotten corrupted during download,\n" + "but we're loading it anyways.", artifactLogName); } @@ -389,14 +615,14 @@ public class DependencyLoaderImpl { for (val checksumType : CHECKSUM_TYPES) { val checksumURL = url + "." + checksumType; val checksumFile = new File(libDir, jarName + "." + checksumType); - log.debug("Attempting to get {} checksum...", checksumType); + LOG.debug("Attempting to get {} checksum...", checksumType); val success = new AtomicBoolean(false); Internet.connect(new URL(checksumURL), - (ex) -> log.debug("Could not get {} checksum for {}: {}", checksumType, + (ex) -> LOG.debug("Could not get {} checksum for {}: {}", checksumType, artifactLogName, ex.getMessage()), (input) -> { - log.debug("Downloading {} checksum for {}", checksumType, artifactLogName); + LOG.debug("Downloading {} checksum for {}", checksumType, artifactLogName); download(input, checksumFile); - log.debug("Downloaded {} checksum for {}", checksumType, artifactLogName); + LOG.debug("Downloaded {} checksum for {}", checksumType, artifactLogName); success.set(true); }); if (success.get()) { @@ -409,7 +635,7 @@ public class DependencyLoaderImpl { private ChecksumStatus validateChecksum(File file) throws IOException { for (val checksumType : CHECKSUM_TYPES) { val checksumFile = new File(libDir, jarName + "." + checksumType); - log.debug("Attempting to read {} checksum from file...", checksumType); + LOG.debug("Attempting to read {} checksum from file...", checksumType); if (checksumFile.exists()) { return getChecksumStatus(file, checksumType, checksumFile); } @@ -421,15 +647,19 @@ public class DependencyLoaderImpl { val fileHash = hash(checksumType, file); val referenceHash = new String(Files.readAllBytes(checksumFile.toPath())); if (!fileHash.equals(referenceHash)) { - log.error("Failed {} checksum validation for {}.", checksumType, artifactLogName); + LOG.error("Failed {} checksum validation for {}.", checksumType, artifactLogName); checkedDelete(file); checkedDelete(checksumFile); return ChecksumStatus.FAILED; } - log.debug("Successfully validated {} checksum for {}.", checksumType, artifactLogName); + LOG.debug("Successfully validated {} checksum for {}.", checksumType, artifactLogName); return ChecksumStatus.OK; } + public String getGroupArtifact() { + return groupId + ":" + artifactId; + } + private enum ChecksumStatus { OK, FAILED, diff --git a/src/main/java/com/falsepattern/lib/internal/impl/dependencies/Pair.java b/src/main/java/com/falsepattern/lib/internal/impl/dependencies/Pair.java new file mode 100644 index 0000000..36ea19d --- /dev/null +++ b/src/main/java/com/falsepattern/lib/internal/impl/dependencies/Pair.java @@ -0,0 +1,32 @@ +/* + * Copyright (C) 2022 FalsePattern + * All Rights Reserved + * + * The above copyright notice, this permission notice and the word "SNEED" + * shall be included in all copies or substantial portions of the Software. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + */ + +package com.falsepattern.lib.internal.impl.dependencies; + +public class Pair { + public final A a; + public final B b; + + public Pair(A a, B b) { + this.a = a; + this.b = b; + } +} diff --git a/src/main/java/com/falsepattern/lib/internal/impl/updates/UpdateCheckerImpl.java b/src/main/java/com/falsepattern/lib/internal/impl/updates/UpdateCheckerImpl.java index 1fc94df..e5d65b2 100644 --- a/src/main/java/com/falsepattern/lib/internal/impl/updates/UpdateCheckerImpl.java +++ b/src/main/java/com/falsepattern/lib/internal/impl/updates/UpdateCheckerImpl.java @@ -21,16 +21,14 @@ package com.falsepattern.lib.internal.impl.updates; -import com.falsepattern.json.node.JsonNode; -import com.falsepattern.lib.dependencies.DependencyLoader; -import com.falsepattern.lib.dependencies.Library; -import com.falsepattern.lib.dependencies.SemanticVersion; import com.falsepattern.lib.internal.Internet; -import com.falsepattern.lib.internal.Tags; import com.falsepattern.lib.internal.config.LibraryConfig; import com.falsepattern.lib.text.FormattedText; import com.falsepattern.lib.updates.ModUpdateInfo; import com.falsepattern.lib.updates.UpdateCheckException; +import com.google.gson.JsonArray; +import com.google.gson.JsonElement; +import com.google.gson.JsonParser; import lombok.val; import net.minecraft.client.resources.I18n; @@ -97,65 +95,49 @@ public final class UpdateCheckerImpl { } catch (MalformedURLException e) { throw new CompletionException(new UpdateCheckException("Invalid URL: " + url, e)); } - if (!jsonLibraryLoaded.get()) { - try { - DependencyLoader.addMavenRepo("https://mvn.falsepattern.com/releases/"); - DependencyLoader.loadLibraries(Library.builder() - .loadingModId(Tags.MODID) - .groupId("com.falsepattern") - .artifactId("json") - .minVersion(new SemanticVersion(0, 4, 0)) - .maxVersion(new SemanticVersion(0, Integer.MAX_VALUE, - Integer.MAX_VALUE)) - .preferredVersion(new SemanticVersion(0, 4, 1)) - .build()); - } catch (Exception e) { - throw new CompletionException( - new UpdateCheckException("Failed to load json library for update checker!", e)); - } - jsonLibraryLoaded.set(true); - } val result = new ArrayList(); - JsonNode parsed; + JsonElement parsed; try { - parsed = JsonNode.parse(Internet.download(URL).thenApply(String::new).join()); + parsed = new JsonParser().parse(Internet.download(URL).thenApply(String::new).join()); } catch (CompletionException e) { throw new CompletionException(new UpdateCheckException("Failed to download update checker JSON file!", e.getCause() == null ? e : e.getCause())); } - List modList; - if (parsed.isList()) { - modList = parsed.getJavaList(); + JsonArray modList; + if (parsed.isJsonArray()) { + modList = parsed.getAsJsonArray(); } else { - modList = Collections.singletonList(parsed); + modList = new JsonArray(); + modList.add(parsed); } val installedMods = Loader.instance().getIndexedModList(); for (val node : modList) { - if (!node.isObject()) { + if (!node.isJsonObject()) { continue; } - if (!node.containsKey("modid")) { + val obj = node.getAsJsonObject(); + if (!obj.has("modid")) { continue; } - if (!node.containsKey("latestVersion")) { + if (!obj.has("latestVersion")) { continue; } - val modid = node.getString("modid"); + val modid = obj.get("modid").getAsString(); if (!installedMods.containsKey(modid)) { continue; } val mod = installedMods.get(modid); - val latestVersionsNode = node.get("latestVersion"); + val latestVersionsNode = obj.get("latestVersion"); List latestVersions; - if (latestVersionsNode.isString()) { - latestVersions = Collections.singletonList(latestVersionsNode.stringValue()); - } else if (latestVersionsNode.isList()) { + if (latestVersionsNode.isJsonPrimitive() && (latestVersionsNode.getAsJsonPrimitive().isString())) { + latestVersions = Collections.singletonList(latestVersionsNode.getAsString()); + } else if (latestVersionsNode.isJsonArray()) { latestVersions = new ArrayList<>(); - for (val version : latestVersionsNode.getJavaList()) { - if (!version.isString()) { + for (val version : latestVersionsNode.getAsJsonArray()) { + if (!version.isJsonPrimitive() || !version.getAsJsonPrimitive().isString()) { continue; } - latestVersions.add(version.stringValue()); + latestVersions.add(version.getAsString()); } } else { continue; @@ -164,9 +146,11 @@ public final class UpdateCheckerImpl { if (latestVersions.contains(currentVersion)) { continue; } - val updateURL = - node.containsKey("updateURL") && node.get("updateURL").isString() ? node.getString("updateURL") - : ""; + val updateURL = obj.has("updateURL") && + obj.get("updateURL").isJsonPrimitive() && + obj.get("updateURL").getAsJsonPrimitive().isString() + ? obj.get("updateURL").getAsString() + : ""; result.add(new ModUpdateInfo(modid, currentVersion, latestVersions.get(0), updateURL)); } return result; diff --git a/src/main/java/com/falsepattern/lib/mapping/MappingManager.java b/src/main/java/com/falsepattern/lib/mapping/MappingManager.java index 81907e8..458943b 100644 --- a/src/main/java/com/falsepattern/lib/mapping/MappingManager.java +++ b/src/main/java/com/falsepattern/lib/mapping/MappingManager.java @@ -21,10 +21,6 @@ package com.falsepattern.lib.mapping; import com.falsepattern.lib.StableAPI; -import com.falsepattern.lib.dependencies.DependencyLoader; -import com.falsepattern.lib.dependencies.Library; -import com.falsepattern.lib.dependencies.SemanticVersion; -import com.falsepattern.lib.internal.Tags; import com.falsepattern.lib.internal.asm.CoreLoadingPlugin; import com.falsepattern.lib.mapping.storage.Lookup; import com.falsepattern.lib.mapping.types.MappingType; @@ -38,9 +34,7 @@ import lombok.val; import lombok.var; import org.objectweb.asm.tree.FieldInsnNode; import org.objectweb.asm.tree.MethodInsnNode; -import org.tukaani.xz.LZMA2Options; -import java.io.DataInputStream; import java.util.HashMap; import java.util.Map; @@ -59,54 +53,24 @@ public class MappingManager { } initialized = true; } - DependencyLoader.addMavenRepo("https://repo1.maven.org/maven2/"); - DependencyLoader.loadLibraries(Library.builder() - .groupId("org.tukaani") - .artifactId("xz") - .minVersion(SemanticVersion.builder() - .majorVersion(1) - .minorVersion(9) - .patchVersion(-1) - .build()) - .preferredVersion(SemanticVersion.builder() - .majorVersion(1) - .minorVersion(9) - .patchVersion(-1) - .build()) - .loadingModId(Tags.MODID) - .build()); - val input = new DataInputStream(new LZMA2Options(6).getInputStream( - ResourceUtil.getResourceFromJar("/mappings.lzma2", CoreLoadingPlugin.class))); - { - var classBytes = new byte[input.readInt()]; - input.readFully(classBytes); - val classMappings = new String(classBytes).split("\n"); - for (int i = 1; i < classMappings.length; i++) { - val line = classMappings[i].split(","); - val clazz = new UniversalClass(line, stringPool); - internalLookup.unwrap(clazz.internalName, clazz); - regularLookup.unwrap(clazz.regularName, clazz); - } + val classMappings = new String(ResourceUtil.getResourceBytesFromJar("/classes.csv", CoreLoadingPlugin.class)).split("\n"); + for (int i = 1; i < classMappings.length; i++) { + val line = classMappings[i].split(","); + val clazz = new UniversalClass(line, stringPool); + internalLookup.unwrap(clazz.internalName, clazz); + regularLookup.unwrap(clazz.regularName, clazz); } - { - var fieldBytes = new byte[input.readInt()]; - input.readFully(fieldBytes); - var fieldMappings = new String(fieldBytes).split("\n"); - for (int i = 1; i < fieldMappings.length; i++) { - val line = fieldMappings[i].split(","); - val clazz = internalLookup.get(MappingType.Notch, line[0].substring(0, line[0].lastIndexOf('/'))); - UniversalField.createAndAddToParent(clazz, line, stringPool); - } + var fieldMappings = new String(ResourceUtil.getResourceBytesFromJar("/fields.csv", CoreLoadingPlugin.class)).split("\n"); + for (int i = 1; i < fieldMappings.length; i++) { + val line = fieldMappings[i].split(","); + val clazz = internalLookup.get(MappingType.Notch, line[0].substring(0, line[0].lastIndexOf('/'))); + UniversalField.createAndAddToParent(clazz, line, stringPool); } - { - var methodBytes = new byte[input.readInt()]; - input.readFully(methodBytes); - val methodMappings = new String(methodBytes).split("\n"); - for (int i = 1; i < methodMappings.length; i++) { - val line = methodMappings[i].split(","); - val clazz = internalLookup.get(MappingType.Notch, line[0].substring(0, line[0].lastIndexOf('/'))); - UniversalMethod.createAndAddToParent(clazz, line, stringPool); - } + val methodMappings = new String(ResourceUtil.getResourceBytesFromJar("/methods.csv", CoreLoadingPlugin.class)).split("\n"); + for (int i = 1; i < methodMappings.length; i++) { + val line = methodMappings[i].split(","); + val clazz = internalLookup.get(MappingType.Notch, line[0].substring(0, line[0].lastIndexOf('/'))); + UniversalMethod.createAndAddToParent(clazz, line, stringPool); } } diff --git a/src/main/resources/DEPENDENCIES.md b/src/main/resources/DEPENDENCIES.md new file mode 100644 index 0000000..a893271 --- /dev/null +++ b/src/main/resources/DEPENDENCIES.md @@ -0,0 +1,55 @@ +FalsePatternLib JSON dependency file example +Version 1 + +An example JSON file that can be used to download dependencies using FalsePatternLib. This replaces the legacy +DependencyLoader api. +```json +{ + "identifier": "falsepatternlib_dependencies", + "repositories": [ + "https://example.com/" + ], + "dependencies": { + "always": { + "common": [], + "client": [], + "server": [] + }, + "obf": { + "common": [ + "com.example:examplejar:1.0.0" + ], + "client": [], + "server": [] + }, + "dev": { + "common": [], + "client": [], + "server": [] + } + } +} +``` + +Explanation: +- `identifier`: The identifier of the json file. This must always be `falsepatternlib_dependencies` for the library downloader + to recognize it. +- `repositories`: A list of maven repositories to use when downloading dependencies. These are used in addition to the + default maven repositories. This is just a list of strings, each string being a https repository url. +- `dependencies`: A categorized list of dependencies. + - The `always` category gets downloaded both inside and outside the dev environment. Usually not needed, as gradle + will automatically download dependencies. + - The `obf` category gets downloaded only in the obfuscated environment. Any third party libraries that are not + available by default from minecraft or forge, AND are also not shaded/shadowed into the jar, should be placed here. + - The `dev` category gets downloaded only in the dev environment. Usually not needed, as gradle will automatically + download dependencies. + +Each of these categories also have 3 more subcategories: +- `common`: Dependencies that are required on both the client and the server. +- `client`: Dependencies that are required on the client only. +- `server`: Dependencies that are required on the server only. + +Each of these subcategories is a list of strings, each string being a maven dependency string. These are the same as +the ones used in the gradle dependencies block. Note that this DOES NOT support the version range syntax, so you must +specify a specific version. For example, `com.example:examplejar:1.0.0` is valid, but `com.example:examplejar:[1.0.0,2.0.0]` +is not. \ No newline at end of file diff --git a/mappings/classes.csv b/src/main/resources/classes.csv similarity index 100% rename from mappings/classes.csv rename to src/main/resources/classes.csv diff --git a/mappings/fields.csv b/src/main/resources/fields.csv similarity index 100% rename from mappings/fields.csv rename to src/main/resources/fields.csv diff --git a/src/main/resources/mappings.lzma2 b/src/main/resources/mappings.lzma2 deleted file mode 100644 index 0de3f11..0000000 Binary files a/src/main/resources/mappings.lzma2 and /dev/null differ diff --git a/mappings/methods.csv b/src/main/resources/methods.csv similarity index 100% rename from mappings/methods.csv rename to src/main/resources/methods.csv diff --git a/src/test/java/com/falsepattern/lib/internal/tooling/Compact.java b/src/test/java/com/falsepattern/lib/internal/tooling/Compact.java deleted file mode 100644 index 8d02b0e..0000000 --- a/src/test/java/com/falsepattern/lib/internal/tooling/Compact.java +++ /dev/null @@ -1,50 +0,0 @@ -/* - * Copyright (C) 2022 FalsePattern - * All Rights Reserved - * - * The above copyright notice, this permission notice and the word "SNEED" - * shall be included in all copies or substantial portions of the Software. - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this program. If not, see . - */ - -package com.falsepattern.lib.internal.tooling; - -import org.tukaani.xz.FinishableOutputStream; -import org.tukaani.xz.FinishableWrapperOutputStream; -import org.tukaani.xz.LZMA2Options; - -import java.io.DataOutputStream; -import java.io.IOException; -import java.nio.file.Files; -import java.nio.file.Paths; - -public class Compact { - public static void main(String[] args) throws IOException { - byte[] file1 = Files.readAllBytes(Paths.get("mappings", "classes.csv")); - byte[] file2 = Files.readAllBytes(Paths.get("mappings", "fields.csv")); - byte[] file3 = Files.readAllBytes(Paths.get("mappings", "methods.csv")); - FinishableOutputStream lzOut = new LZMA2Options(6).getOutputStream(new FinishableWrapperOutputStream( - Files.newOutputStream(Paths.get("src", "main", "resources", "mappings.lzma2")))); - DataOutputStream dOut = new DataOutputStream(lzOut); - dOut.writeInt(file1.length); - dOut.write(file1); - dOut.writeInt(file2.length); - dOut.write(file2); - dOut.writeInt(file3.length); - dOut.write(file3); - lzOut.finish(); - lzOut.close(); - } -}