From ea44973702d6f26d4095ec7d7622506ad473f78c Mon Sep 17 00:00:00 2001 From: Jonas Herzig Date: Thu, 6 Jan 2022 13:52:59 +0100 Subject: [PATCH 01/44] Simplify `MinecraftPatchedProvider.getGlobalCaches()` Way easier to read than the previous array-copy implementation and trivial to extend when more file are optional (see next commit). --- .../providers/forge/MinecraftPatchedProvider.java | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/src/main/java/net/fabricmc/loom/configuration/providers/forge/MinecraftPatchedProvider.java b/src/main/java/net/fabricmc/loom/configuration/providers/forge/MinecraftPatchedProvider.java index fadf7d337..9313fcee1 100644 --- a/src/main/java/net/fabricmc/loom/configuration/providers/forge/MinecraftPatchedProvider.java +++ b/src/main/java/net/fabricmc/loom/configuration/providers/forge/MinecraftPatchedProvider.java @@ -223,14 +223,9 @@ private File[] getGlobalCaches() { minecraftServerPatchedSrgJar, minecraftMergedPatchedSrgJar, minecraftClientExtra, + forgeMergedJar }; - - if (forgeMergedJar != null) { - Arrays.copyOf(files, files.length + 1); - files[files.length - 1] = forgeMergedJar; - } - - return files; + return Arrays.stream(files).filter(Objects::nonNull).toArray(File[]::new); } public void cleanProjectCache() { From 6ff92118c3621bdddb9d35fb537a96040e08ca01 Mon Sep 17 00:00:00 2001 From: Jonas Herzig Date: Thu, 6 Jan 2022 13:55:24 +0100 Subject: [PATCH 02/44] Fix always-dirty cache due to forge-client-extra.jar This jar used to always be included in the cache file list even though it is only used/generated with official mappings. Therefore it would always be missing from the cache when not using official mappings, unnecessarily re-running the patching code. --- .../configuration/providers/forge/MinecraftPatchedProvider.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/net/fabricmc/loom/configuration/providers/forge/MinecraftPatchedProvider.java b/src/main/java/net/fabricmc/loom/configuration/providers/forge/MinecraftPatchedProvider.java index 9313fcee1..0c089e8f9 100644 --- a/src/main/java/net/fabricmc/loom/configuration/providers/forge/MinecraftPatchedProvider.java +++ b/src/main/java/net/fabricmc/loom/configuration/providers/forge/MinecraftPatchedProvider.java @@ -186,7 +186,7 @@ public void initFiles() throws IOException { forgeMergedJar = getExtension().isForgeAndOfficial() ? null : new File(globalCache, "forge-official.jar"); minecraftMergedPatchedSrgAtJar = new File(projectDir, "merged-srg-at-patched.jar"); minecraftMergedPatchedJar = new File(projectDir, "merged-patched.jar"); - minecraftClientExtra = new File(globalCache, "forge-client-extra.jar"); + minecraftClientExtra = getExtension().isForgeAndOfficial() ? new File(globalCache, "forge-client-extra.jar") : null; if (isRefreshDeps() || Stream.of(getGlobalCaches()).anyMatch(((Predicate) File::exists).negate()) || !isPatchedJarUpToDate(minecraftMergedPatchedJar)) { From 64a1aba1db539ea854fdc832b1a8e53ac7428263 Mon Sep 17 00:00:00 2001 From: Jonas Herzig Date: Thu, 6 Jan 2022 14:01:18 +0100 Subject: [PATCH 03/44] Fix missing SrgProvider when generateSrgTiny is false A fallback SrgProvider is instantiated and initialized here but never added to the provider list, resulting in a NPE right below in `getRawSrgFile`. --- .../configuration/providers/mappings/MappingsProviderImpl.java | 1 + 1 file changed, 1 insertion(+) diff --git a/src/main/java/net/fabricmc/loom/configuration/providers/mappings/MappingsProviderImpl.java b/src/main/java/net/fabricmc/loom/configuration/providers/mappings/MappingsProviderImpl.java index 23bf71c83..0f4614ce5 100644 --- a/src/main/java/net/fabricmc/loom/configuration/providers/mappings/MappingsProviderImpl.java +++ b/src/main/java/net/fabricmc/loom/configuration/providers/mappings/MappingsProviderImpl.java @@ -340,6 +340,7 @@ private void readAndMergeMCP(Path mcpJar, Consumer postPopulationSched getProject().getDependencies().add(provider.getTargetConfig(), "de.oceanlabs.mcp:mcp_config:" + getMinecraftProvider().minecraftVersion()); Configuration configuration = getProject().getConfigurations().getByName(provider.getTargetConfig()); provider.provide(DependencyInfo.create(getProject(), configuration.getDependencies().iterator().next(), configuration), postPopulationScheduler); + getExtension().getDependencyManager().addProvider(provider); } Path srgPath = getRawSrgFile(); From f0cafe844107eb484e9d769dd0cad7c7e5f27e9d Mon Sep 17 00:00:00 2001 From: Jonas Herzig Date: Thu, 6 Jan 2022 16:31:13 +0100 Subject: [PATCH 04/44] Support for FG2-era Forge --- build.gradle | 4 + .../fabricmc/loom/LoomGradleExtension.java | 8 + .../fabricmc/loom/LoomRepositoryPlugin.java | 14 + .../fabricmc/loom/api/ForgeExtensionAPI.java | 4 + .../loom/build/ModCompileRemapper.java | 2 +- .../mixin/AnnotationProcessorInvoker.java | 2 +- .../configuration/CompileConfiguration.java | 2 +- .../providers/LaunchProvider.java | 28 +- .../forge/FieldMigratedMappingsProvider.java | 9 +- .../providers/forge/ForgeUserdevProvider.java | 53 ++- .../providers/forge/McpConfigProvider.java | 5 + .../forge/MinecraftPatchedProvider.java | 32 +- .../providers/forge/PatchProvider.java | 98 +++- .../providers/forge/SrgProvider.java | 14 +- .../fg2/MinecraftLegacyPatchedProvider.java | 447 ++++++++++++++++++ .../providers/forge/fg2/Pack200Provider.java | 8 + .../mappings/MappingsProviderImpl.java | 55 ++- .../minecraft/MinecraftMappedProvider.java | 12 +- .../loom/extension/ForgeExtensionImpl.java | 8 + .../net/fabricmc/loom/util/Constants.java | 6 + .../net/fabricmc/loom/util/LoggerFilter.java | 28 ++ .../net/fabricmc/loom/util/srg/SrgMerger.java | 16 +- .../loom/util/srg/SrgNamedWriter.java | 36 +- .../test/integration/forge/ForgeTest.groovy | 2 + .../projects/forge/simple/build.gradle | 7 + 25 files changed, 852 insertions(+), 48 deletions(-) create mode 100644 src/main/java/net/fabricmc/loom/configuration/providers/forge/fg2/MinecraftLegacyPatchedProvider.java create mode 100644 src/main/java/net/fabricmc/loom/configuration/providers/forge/fg2/Pack200Provider.java diff --git a/build.gradle b/build.gradle index 2cc27e478..ef0a1da80 100644 --- a/build.gradle +++ b/build.gradle @@ -119,6 +119,9 @@ dependencies { implementation ('de.oceanlabs.mcp:mcinjector:3.8.0') implementation ('com.opencsv:opencsv:5.4') + // Legacy Forge access transformers + implementation ('net.md-5:SpecialSource:1.10.0') + // Testing testImplementation(gradleTestKit()) testImplementation('org.spockframework:spock-core:2.0-groovy-3.0') { @@ -126,6 +129,7 @@ dependencies { } testImplementation 'io.javalin:javalin:3.13.11' testImplementation 'net.fabricmc:fabric-installer:0.9.0' + runtimeOnly 'dev.architectury.architectury-pack200:dev.architectury.architectury-pack200.gradle.plugin:0.1.3' compileOnly 'org.jetbrains:annotations:22.0.0' } diff --git a/src/main/java/net/fabricmc/loom/LoomGradleExtension.java b/src/main/java/net/fabricmc/loom/LoomGradleExtension.java index 85855067b..b5566765f 100644 --- a/src/main/java/net/fabricmc/loom/LoomGradleExtension.java +++ b/src/main/java/net/fabricmc/loom/LoomGradleExtension.java @@ -139,6 +139,14 @@ default boolean isForgeAndNotOfficial() { return isForge() && !getMcpConfigProvider().isOfficial(); } + default boolean isLegacyForge() { + return isForge() && getForgeUserdevProvider().isLegacyForge(); + } + + default boolean isModLauncher() { + return isForge() && !isLegacyForge(); + } + boolean supportsInclude(); default SrgProvider getSrgProvider() { diff --git a/src/main/java/net/fabricmc/loom/LoomRepositoryPlugin.java b/src/main/java/net/fabricmc/loom/LoomRepositoryPlugin.java index 280450717..3d93bad6b 100644 --- a/src/main/java/net/fabricmc/loom/LoomRepositoryPlugin.java +++ b/src/main/java/net/fabricmc/loom/LoomRepositoryPlugin.java @@ -90,6 +90,20 @@ private void declareRepositories(RepositoryHandler repositories, LoomFiles files sources.ignoreGradleMetadataRedirection(); }); }); + repositories.ivy(repo -> { + // Old MCP data does not have POMs + repo.setName("LegacyMCP"); + repo.setUrl("https://maven.minecraftforge.net/"); + repo.patternLayout(layout -> { + layout.artifact("[orgPath]/[artifact]/[revision]/[artifact]-[revision](-[classifier])(.[ext])"); + // also check the zip so people do not have to explicitly specify the extension for older versions + layout.artifact("[orgPath]/[artifact]/[revision]/[artifact]-[revision](-[classifier]).zip"); + }); + repo.content(descriptor -> { + descriptor.includeGroup("de.oceanlabs.mcp"); + }); + repo.metadataSources(IvyArtifactRepository.MetadataSources::artifact); + }); repositories.mavenCentral(); repositories.ivy(repo -> { diff --git a/src/main/java/net/fabricmc/loom/api/ForgeExtensionAPI.java b/src/main/java/net/fabricmc/loom/api/ForgeExtensionAPI.java index e455d742f..6b77f8420 100644 --- a/src/main/java/net/fabricmc/loom/api/ForgeExtensionAPI.java +++ b/src/main/java/net/fabricmc/loom/api/ForgeExtensionAPI.java @@ -33,6 +33,8 @@ import org.gradle.api.provider.SetProperty; import org.jetbrains.annotations.ApiStatus; +import net.fabricmc.loom.configuration.providers.forge.fg2.Pack200Provider; + /** * This is the forge extension api available exposed to build scripts. */ @@ -148,4 +150,6 @@ interface DataGenConsumer { * @see ForgeLocalMod */ NamedDomainObjectContainer getLocalMods(); + + Property getPack200Provider(); } diff --git a/src/main/java/net/fabricmc/loom/build/ModCompileRemapper.java b/src/main/java/net/fabricmc/loom/build/ModCompileRemapper.java index 76475caaf..6fe72b189 100644 --- a/src/main/java/net/fabricmc/loom/build/ModCompileRemapper.java +++ b/src/main/java/net/fabricmc/loom/build/ModCompileRemapper.java @@ -181,7 +181,7 @@ private static boolean shouldRemapMod(Logger logger, File artifact, Object id, b } if (forge) { - if (zipFile.getEntry("META-INF/mods.toml") != null) { + if (zipFile.getEntry("META-INF/mods.toml") != null || zipFile.getEntry("mcmod.info") != null) { logger.info("Found Forge mod in " + config + ": {}", id); return true; } diff --git a/src/main/java/net/fabricmc/loom/build/mixin/AnnotationProcessorInvoker.java b/src/main/java/net/fabricmc/loom/build/mixin/AnnotationProcessorInvoker.java index 33fec4e6e..1793ca725 100644 --- a/src/main/java/net/fabricmc/loom/build/mixin/AnnotationProcessorInvoker.java +++ b/src/main/java/net/fabricmc/loom/build/mixin/AnnotationProcessorInvoker.java @@ -101,7 +101,7 @@ public void configureMixin() { ConfigurationContainer configs = project.getConfigurations(); LoomGradleExtension extension = LoomGradleExtension.get(project); - if (!extension.ideSync()) { + if (!extension.ideSync() || extension.isLegacyForge()) { for (Configuration processorConfig : apConfigurations) { project.getLogger().info("Adding mixin to classpath of AP config: " + processorConfig.getName()); // Pass named MC classpath to mixin AP classpath diff --git a/src/main/java/net/fabricmc/loom/configuration/CompileConfiguration.java b/src/main/java/net/fabricmc/loom/configuration/CompileConfiguration.java index eaac38ad3..50febf4ed 100644 --- a/src/main/java/net/fabricmc/loom/configuration/CompileConfiguration.java +++ b/src/main/java/net/fabricmc/loom/configuration/CompileConfiguration.java @@ -198,9 +198,9 @@ public static void configureCompile(Project p) { } if (extension.isForge()) { + dependencyManager.addProvider(new ForgeUniversalProvider(project)); dependencyManager.addProvider(new McpConfigProvider(project)); dependencyManager.addProvider(new PatchProvider(project)); - dependencyManager.addProvider(new ForgeUniversalProvider(project)); } dependencyManager.addProvider(extension.isForge() ? new FieldMigratedMappingsProvider(project) : new MappingsProviderImpl(project)); diff --git a/src/main/java/net/fabricmc/loom/configuration/providers/LaunchProvider.java b/src/main/java/net/fabricmc/loom/configuration/providers/LaunchProvider.java index 2ea5c812f..ae52c76ff 100644 --- a/src/main/java/net/fabricmc/loom/configuration/providers/LaunchProvider.java +++ b/src/main/java/net/fabricmc/loom/configuration/providers/LaunchProvider.java @@ -31,7 +31,7 @@ import java.nio.file.Files; import java.util.ArrayList; import java.util.Arrays; -import java.util.HashMap; +import java.util.LinkedHashMap; import java.util.List; import java.util.Map; import java.util.Set; @@ -66,7 +66,7 @@ public void provide(DependencyInfo dependency, Consumer postPopulation .property("client", "java.library.path", getExtension().getMinecraftProvider().nativesDir().getAbsolutePath()) .property("client", "org.lwjgl.librarypath", getExtension().getMinecraftProvider().nativesDir().getAbsolutePath()); - if (!getExtension().isForge()) { + if (!getExtension().isModLauncher()) { launchConfig .argument("client", "--assetIndex") .argument("client", getExtension().getMinecraftProvider().getVersionInfo().assetIndex().fabricId(getExtension().getMinecraftProvider().minecraftVersion())) @@ -74,7 +74,7 @@ public void provide(DependencyInfo dependency, Consumer postPopulation .argument("client", new File(getDirectories().getUserCache(), "assets").getAbsolutePath()); } - if (getExtension().isForge()) { + if (getExtension().isModLauncher()) { launchConfig // Should match YarnNamingService.PATH_TO_MAPPINGS in forge-runtime .property("fabric.yarnWithSrg.path", getExtension().getMappingsProvider().tinyMappingsWithSrg.toAbsolutePath().toString()) @@ -103,11 +103,29 @@ public void provide(DependencyInfo dependency, Consumer postPopulation } } + if (getExtension().isLegacyForge()) { + launchConfig + .argument("client", "--tweakClass") + .argument("client", Constants.LegacyForge.FML_TWEAKER) + .argument("server", "--tweakClass") + .argument("server", Constants.LegacyForge.FML_SERVER_TWEAKER) + + .argument("--accessToken") + .argument("undefined") + + .property("net.minecraftforge.gradle.GradleStart.srg.srg-mcp", getExtension().getMappingsProvider().srgToNamedSrg.toAbsolutePath().toString()) + .property("mixin.env.remapRefMap", "true"); + + for (String config : PropertyUtil.getAndFinalize(getExtension().getForge().getMixinConfigs())) { + launchConfig.argument("--mixin").argument(config); + } + } + addDependency(Constants.Dependencies.DEV_LAUNCH_INJECTOR + Constants.Dependencies.Versions.DEV_LAUNCH_INJECTOR, Constants.Configurations.LOOM_DEVELOPMENT_DEPENDENCIES); addDependency(Constants.Dependencies.TERMINAL_CONSOLE_APPENDER + Constants.Dependencies.Versions.TERMINAL_CONSOLE_APPENDER, Constants.Configurations.LOOM_DEVELOPMENT_DEPENDENCIES); addDependency(Constants.Dependencies.JETBRAINS_ANNOTATIONS + Constants.Dependencies.Versions.JETBRAINS_ANNOTATIONS, JavaPlugin.COMPILE_ONLY_CONFIGURATION_NAME); - if (getExtension().isForge()) { + if (getExtension().isForge() && !getExtension().isLegacyForge()) { addDependency(Constants.Dependencies.FORGE_RUNTIME + Constants.Dependencies.Versions.FORGE_RUNTIME, Constants.Configurations.FORGE_EXTRA); addDependency(Constants.Dependencies.JAVAX_ANNOTATIONS + Constants.Dependencies.Versions.JAVAX_ANNOTATIONS, JavaPlugin.COMPILE_ONLY_CONFIGURATION_NAME); } @@ -197,7 +215,7 @@ public String getTargetConfig() { } public static class LaunchConfig { - private final Map> values = new HashMap<>(); + private final Map> values = new LinkedHashMap<>(); public LaunchConfig property(String key, String value) { return property("common", key, value); diff --git a/src/main/java/net/fabricmc/loom/configuration/providers/forge/FieldMigratedMappingsProvider.java b/src/main/java/net/fabricmc/loom/configuration/providers/forge/FieldMigratedMappingsProvider.java index b2ebfa714..aa041162f 100644 --- a/src/main/java/net/fabricmc/loom/configuration/providers/forge/FieldMigratedMappingsProvider.java +++ b/src/main/java/net/fabricmc/loom/configuration/providers/forge/FieldMigratedMappingsProvider.java @@ -109,6 +109,13 @@ protected String createMappingsIdentifier(String mappingsName, String version, S public void manipulateMappings(Path mappingsJar) throws IOException { Stopwatch stopwatch = Stopwatch.createStarted(); LoomGradleExtension extension = getExtension(); + + if (extension.isLegacyForge()) { + // Legacy forge patches are in official namespace, so if the type of a field is changed by them, then that + // is effectively a new field and not traceable to any mapping. Therefore this does not apply to it. + return; + } + this.rawTinyMappings = tinyMappings; this.rawTinyMappingsWithSrg = tinyMappingsWithSrg; String mappingsJarName = mappingsJar.getFileName().toString(); @@ -116,7 +123,7 @@ public void manipulateMappings(Path mappingsJar) throws IOException { if (getExtension().shouldGenerateSrgTiny()) { if (Files.notExists(rawTinyMappingsWithSrg) || isRefreshDeps()) { // Merge tiny mappings with srg - SrgMerger.mergeSrg(getProject().getLogger(), getExtension().getMappingsProvider()::getMojmapSrgFileIfPossible, getRawSrgFile(), rawTinyMappings, rawTinyMappingsWithSrg, true); + SrgMerger.mergeSrg(getProject().getLogger(), getExtension().getMappingsProvider()::getMojmapSrgFileIfPossible, getRawSrgFile(), rawTinyMappings, rawTinyMappingsWithSrg, true, false); } } diff --git a/src/main/java/net/fabricmc/loom/configuration/providers/forge/ForgeUserdevProvider.java b/src/main/java/net/fabricmc/loom/configuration/providers/forge/ForgeUserdevProvider.java index 358fd1109..ec98da784 100644 --- a/src/main/java/net/fabricmc/loom/configuration/providers/forge/ForgeUserdevProvider.java +++ b/src/main/java/net/fabricmc/loom/configuration/providers/forge/ForgeUserdevProvider.java @@ -72,6 +72,7 @@ public class ForgeUserdevProvider extends DependencyProvider { private File userdevJar; private JsonObject json; private Consumer postPopulationScheduler; + private boolean isLegacyForge; public ForgeUserdevProvider(Project project) { super(project); @@ -99,7 +100,13 @@ public void provide(DependencyInfo dependency, Consumer postPopulation Files.copy(resolved.toPath(), userdevJar.toPath(), StandardCopyOption.REPLACE_EXISTING); try (FileSystem fs = FileSystems.newFileSystem(new URI("jar:" + resolved.toURI()), ImmutableMap.of("create", false))) { - Files.copy(fs.getPath("config.json"), configJson, StandardCopyOption.REPLACE_EXISTING); + Path configEntry = fs.getPath("config.json"); + + if (!Files.exists(configEntry)) { + configEntry = fs.getPath("dev.json"); + } + + Files.copy(configEntry, configJson, StandardCopyOption.REPLACE_EXISTING); } } @@ -107,11 +114,30 @@ public void provide(DependencyInfo dependency, Consumer postPopulation json = new Gson().fromJson(reader, JsonObject.class); } - addDependency(json.get("mcp").getAsString(), Constants.Configurations.MCP_CONFIG); - addDependency(json.get("mcp").getAsString(), Constants.Configurations.SRG); - addDependency(json.get("universal").getAsString(), Constants.Configurations.FORGE_UNIVERSAL); + isLegacyForge = !json.has("mcp"); + + if (isLegacyForge) { + Map mcpDep = Map.of( + "group", "de.oceanlabs.mcp", + "name", "mcp", + "version", json.get("inheritsFrom").getAsString(), + "classifier", "srg", + "ext", "zip" + ); + addDependency(mcpDep, Constants.Configurations.MCP_CONFIG); + addDependency(mcpDep, Constants.Configurations.SRG); + addDependency(dependency.getDepString() + ":universal", Constants.Configurations.FORGE_UNIVERSAL); + } else { + addDependency(json.get("mcp").getAsString(), Constants.Configurations.MCP_CONFIG); + addDependency(json.get("mcp").getAsString(), Constants.Configurations.SRG); + addDependency(json.get("universal").getAsString(), Constants.Configurations.FORGE_UNIVERSAL); + } for (JsonElement lib : json.get("libraries").getAsJsonArray()) { + if (isLegacyForge) { + lib = lib.getAsJsonObject().get("name"); + } + Dependency dep = null; if (lib.getAsString().startsWith("org.spongepowered:mixin:")) { @@ -128,7 +154,7 @@ public void provide(DependencyInfo dependency, Consumer postPopulation dep = addDependency(lib.getAsString(), Constants.Configurations.FORGE_DEPENDENCIES); } - if (lib.getAsString().split(":").length < 4) { + if (!isLegacyForge && lib.getAsString().split(":").length < 4) { ((ModuleDependency) dep).attributes(attributes -> { attributes.attribute(transformed, true); }); @@ -137,7 +163,10 @@ public void provide(DependencyInfo dependency, Consumer postPopulation // TODO: Should I copy the patches from here as well? // That'd require me to run the "MCP environment" fully up to merging. - for (Map.Entry entry : json.getAsJsonObject("runs").entrySet()) { + + JsonObject runs = isLegacyForge ? new JsonObject() : json.getAsJsonObject("runs"); + + for (Map.Entry entry : runs.entrySet()) { LaunchProviderSettings launchSettings = getExtension().getLaunchConfigs().findByName(entry.getKey()); RunConfigSettings settings = getExtension().getRunConfigs().findByName(entry.getKey()); JsonObject value = entry.getValue().getAsJsonObject(); @@ -182,6 +211,18 @@ public void provide(DependencyInfo dependency, Consumer postPopulation }); } } + + if (isLegacyForge) { + getExtension().getRunConfigs().configureEach(config -> { + if (Constants.Forge.LAUNCH_TESTING.equals(config.getDefaultMainClass())) { + config.setDefaultMainClass(Constants.LegacyForge.LAUNCH_WRAPPER); + } + }); + } + } + + public boolean isLegacyForge() { + return isLegacyForge; } public abstract static class RemoveNameProvider implements TransformAction { diff --git a/src/main/java/net/fabricmc/loom/configuration/providers/forge/McpConfigProvider.java b/src/main/java/net/fabricmc/loom/configuration/providers/forge/McpConfigProvider.java index 7404cc0a6..160f14d69 100644 --- a/src/main/java/net/fabricmc/loom/configuration/providers/forge/McpConfigProvider.java +++ b/src/main/java/net/fabricmc/loom/configuration/providers/forge/McpConfigProvider.java @@ -64,6 +64,11 @@ public McpConfigProvider(Project project) { @Override public void provide(DependencyInfo dependency, Consumer postPopulationScheduler) throws Exception { + if (getExtension().isLegacyForge()) { + official = false; + return; + } + init(dependency.getDependency().getVersion()); Path mcpZip = dependency.resolveFile().orElseThrow(() -> new RuntimeException("Could not resolve MCPConfig")).toPath(); diff --git a/src/main/java/net/fabricmc/loom/configuration/providers/forge/MinecraftPatchedProvider.java b/src/main/java/net/fabricmc/loom/configuration/providers/forge/MinecraftPatchedProvider.java index 0c089e8f9..05a216bd6 100644 --- a/src/main/java/net/fabricmc/loom/configuration/providers/forge/MinecraftPatchedProvider.java +++ b/src/main/java/net/fabricmc/loom/configuration/providers/forge/MinecraftPatchedProvider.java @@ -120,9 +120,9 @@ public class MinecraftPatchedProvider extends DependencyProvider { private File minecraftClientExtra; private File projectAtHash; - private Set projectAts = new HashSet<>(); - private boolean atDirty = false; - private boolean filesDirty = false; + protected Set projectAts = new HashSet<>(); + protected boolean atDirty = false; + protected boolean filesDirty = false; private Path mcpConfigMappings; private Path[] mergedMojangTsrg2Files; @@ -130,8 +130,7 @@ public MinecraftPatchedProvider(Project project) { super(project); } - public void initFiles() throws IOException { - filesDirty = false; + protected void initAts() throws IOException { projectAtHash = new File(getDirectories().getProjectPersistentCache(), "at.sha256"); ConfigurableFileCollection accessTransformers = getExtension().getForge().getAccessTransformers(); accessTransformers.finalizeValue(); @@ -164,6 +163,11 @@ public void initFiles() throws IOException { atDirty = mismatched; } + } + + public void initFiles() throws IOException { + filesDirty = false; + initAts(); MinecraftProviderImpl minecraftProvider = getExtension().getMinecraftProvider(); PatchProvider patchProvider = getExtension().getPatchProvider(); @@ -215,7 +219,7 @@ public void cleanAllCache() { cleanProjectCache(); } - private File[] getGlobalCaches() { + protected File[] getGlobalCaches() { File[] files = { minecraftClientSrgJar, minecraftServerSrgJar, @@ -234,7 +238,7 @@ public void cleanProjectCache() { } } - private File[] getProjectCache() { + protected File[] getProjectCache() { return new File[] { minecraftMergedPatchedSrgAtJar, minecraftMergedPatchedJar @@ -451,7 +455,7 @@ private File getForgeUserdevJar() { return getExtension().getForgeUserdevProvider().getUserdevJar(); } - private boolean isPatchedJarUpToDate(File jar) throws IOException { + protected boolean isPatchedJarUpToDate(File jar) throws IOException { if (!jar.exists()) return false; byte[] manifestBytes = ZipUtils.unpackNullable(jar.toPath(), "META-INF/MANIFEST.MF"); @@ -594,7 +598,7 @@ private void remapPatchedJar(Logger logger) throws Exception { applyLoomPatchVersion(mcOutput); } - private void patchJars(Logger logger) throws IOException { + private void patchJars(Logger logger) throws Exception { Stopwatch stopwatch = Stopwatch.createStarted(); logger.lifecycle(":patching jars"); @@ -614,7 +618,7 @@ private void patchJars(Logger logger) throws IOException { logger.lifecycle(":patched jars in " + stopwatch.stop()); } - private void patchJars(File clean, File output, Path patches) throws IOException { + protected void patchJars(File clean, File output, Path patches) throws Exception { PrintStream previous = System.out; try { @@ -677,7 +681,7 @@ private void walkFileSystems(File source, File target, Predicate filter, F } } - private void walkFileSystems(File source, File target, Predicate filter, FsPathConsumer action) throws IOException { + protected void walkFileSystems(File source, File target, Predicate filter, FsPathConsumer action) throws IOException { walkFileSystems(source, target, filter, FileSystem::getRootDirectories, action); } @@ -685,7 +689,7 @@ private void copyAll(File source, File target) throws IOException { walkFileSystems(source, target, it -> true, this::copyReplacing); } - private void copyMissingClasses(File source, File target) throws IOException { + protected void copyMissingClasses(File source, File target) throws IOException { walkFileSystems(source, target, it -> it.toString().endsWith(".class"), (sourceFs, targetFs, sourcePath, targetPath) -> { if (Files.exists(targetPath)) return; Path parent = targetPath.getParent(); @@ -698,7 +702,7 @@ private void copyMissingClasses(File source, File target) throws IOException { }); } - private void copyNonClassFiles(File source, File target) throws IOException { + protected void copyNonClassFiles(File source, File target) throws IOException { Predicate filter = getExtension().isForgeAndOfficial() ? file -> { String s = file.toString(); return !s.endsWith(".class"); @@ -710,7 +714,7 @@ private void copyNonClassFiles(File source, File target) throws IOException { walkFileSystems(source, target, filter, this::copyReplacing); } - private void copyReplacing(FileSystem sourceFs, FileSystem targetFs, Path sourcePath, Path targetPath) throws IOException { + protected void copyReplacing(FileSystem sourceFs, FileSystem targetFs, Path sourcePath, Path targetPath) throws IOException { Path parent = targetPath.getParent(); if (parent != null) { diff --git a/src/main/java/net/fabricmc/loom/configuration/providers/forge/PatchProvider.java b/src/main/java/net/fabricmc/loom/configuration/providers/forge/PatchProvider.java index d49ae8b49..ca55e8d7d 100644 --- a/src/main/java/net/fabricmc/loom/configuration/providers/forge/PatchProvider.java +++ b/src/main/java/net/fabricmc/loom/configuration/providers/forge/PatchProvider.java @@ -24,7 +24,16 @@ package net.fabricmc.loom.configuration.providers.forge; +import static java.nio.file.StandardOpenOption.CREATE; +import static java.nio.file.StandardOpenOption.TRUNCATE_EXISTING; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.DataInputStream; +import java.io.DataOutputStream; import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; import java.io.UncheckedIOException; import java.net.URI; import java.nio.file.FileSystem; @@ -33,11 +42,21 @@ import java.nio.file.Path; import java.nio.file.StandardCopyOption; import java.util.function.Consumer; +import java.util.jar.JarEntry; +import java.util.jar.JarInputStream; +import java.util.jar.JarOutputStream; +import java.util.zip.ZipEntry; import com.google.common.collect.ImmutableMap; +import lzma.sdk.lzma.Decoder; +import lzma.sdk.lzma.Encoder; +import lzma.streams.LzmaInputStream; +import lzma.streams.LzmaOutputStream; +import org.apache.commons.io.IOUtils; import org.gradle.api.Project; import net.fabricmc.loom.configuration.DependencyProvider; +import net.fabricmc.loom.configuration.providers.forge.fg2.Pack200Provider; import net.fabricmc.loom.util.Constants; public class PatchProvider extends DependencyProvider { @@ -56,11 +75,17 @@ public void provide(DependencyInfo dependency, Consumer postPopulation if (Files.notExists(clientPatches) || Files.notExists(serverPatches) || isRefreshDeps()) { getProject().getLogger().info(":extracting forge patches"); - Path installerJar = dependency.resolveFile().orElseThrow(() -> new RuntimeException("Could not resolve Forge installer")).toPath(); + Path installerJar = getExtension().isLegacyForge() + ? getExtension().getForgeUniversalProvider().getForge().toPath() + : dependency.resolveFile().orElseThrow(() -> new RuntimeException("Could not resolve Forge installer")).toPath(); try (FileSystem fs = FileSystems.newFileSystem(new URI("jar:" + installerJar.toUri()), ImmutableMap.of("create", false))) { - Files.copy(fs.getPath("data", "client.lzma"), clientPatches, StandardCopyOption.REPLACE_EXISTING); - Files.copy(fs.getPath("data", "server.lzma"), serverPatches, StandardCopyOption.REPLACE_EXISTING); + if (getExtension().isLegacyForge()) { + splitAndConvertLegacyPatches(fs.getPath("binpatches.pack.lzma")); + } else { + Files.copy(fs.getPath("data", "client.lzma"), clientPatches, StandardCopyOption.REPLACE_EXISTING); + Files.copy(fs.getPath("data", "server.lzma"), serverPatches, StandardCopyOption.REPLACE_EXISTING); + } } } } @@ -77,6 +102,73 @@ private void init(String forgeVersion) { } } + private void splitAndConvertLegacyPatches(Path joinedLegacyPatches) throws IOException { + try (JarInputStream in = new JarInputStream(new ByteArrayInputStream(unpack200Lzma(joinedLegacyPatches))); + OutputStream clientFileOut = Files.newOutputStream(clientPatches, CREATE, TRUNCATE_EXISTING); + LzmaOutputStream clientLzmaOut = new LzmaOutputStream(clientFileOut, new Encoder()); + JarOutputStream clientJarOut = new JarOutputStream(clientLzmaOut); + OutputStream serverFileOut = Files.newOutputStream(serverPatches, CREATE, TRUNCATE_EXISTING); + LzmaOutputStream serverLzmaOut = new LzmaOutputStream(serverFileOut, new Encoder()); + JarOutputStream serverJarOut = new JarOutputStream(serverLzmaOut); + ) { + for (JarEntry entry; (entry = in.getNextJarEntry()) != null;) { + String name = entry.getName(); + + JarOutputStream out; + + if (name.startsWith("binpatch/client/")) { + out = clientJarOut; + } else if (name.startsWith("binpatch/server/")) { + out = serverJarOut; + } else { + getProject().getLogger().warn("Unexpected file in Forge binpatches archive: " + name); + continue; + } + + out.putNextEntry(new ZipEntry(name)); + + // Converting from legacy format to modern (v1) format + DataInputStream dataIn = new DataInputStream(in); + DataOutputStream dataOut = new DataOutputStream(out); + dataOut.writeByte(1); // version + dataIn.readUTF(); // unused patch name (presumably always the same as the obf class name) + dataOut.writeUTF(dataIn.readUTF().replace('.', '/')); // obf class name + dataOut.writeUTF(dataIn.readUTF().replace('.', '/')); // srg class name + IOUtils.copy(in, out); // remainder is unchanged + + out.closeEntry(); + } + } + } + + private byte[] unpack200(InputStream in) throws IOException { + ByteArrayOutputStream bytes = new ByteArrayOutputStream(); + + try (JarOutputStream jarOut = new JarOutputStream(bytes)) { + Pack200Provider provider = getExtension().getForge().getPack200Provider().getOrNull(); + + if (provider == null) { + throw new IllegalStateException("No provider for Pack200 has been found. Did you declare a provider?"); + } + + provider.unpack(in, jarOut); + } + + return bytes.toByteArray(); + } + + private byte[] unpack200Lzma(InputStream in) throws IOException { + try (LzmaInputStream lzmaIn = new LzmaInputStream(in, new Decoder())) { + return unpack200(lzmaIn); + } + } + + private byte[] unpack200Lzma(Path path) throws IOException { + try (InputStream in = Files.newInputStream(path)) { + return unpack200Lzma(in); + } + } + public Path getProjectCacheFolder() { return projectCacheFolder; } diff --git a/src/main/java/net/fabricmc/loom/configuration/providers/forge/SrgProvider.java b/src/main/java/net/fabricmc/loom/configuration/providers/forge/SrgProvider.java index 55a19fd66..920d269ef 100644 --- a/src/main/java/net/fabricmc/loom/configuration/providers/forge/SrgProvider.java +++ b/src/main/java/net/fabricmc/loom/configuration/providers/forge/SrgProvider.java @@ -29,8 +29,10 @@ import java.io.File; import java.io.IOException; import java.io.PrintStream; +import java.io.Reader; import java.io.StringReader; import java.io.UncheckedIOException; +import java.io.Writer; import java.net.URI; import java.nio.charset.StandardCharsets; import java.nio.file.FileSystem; @@ -47,6 +49,8 @@ import com.google.common.collect.ImmutableMap; import org.apache.commons.io.FileUtils; import org.apache.commons.io.output.NullOutputStream; +import org.cadixdev.lorenz.io.srg.SrgReader; +import org.cadixdev.lorenz.io.srg.tsrg.TSrgWriter; import org.gradle.api.Project; import org.gradle.api.logging.LogLevel; @@ -87,7 +91,15 @@ public void provide(DependencyInfo dependency, Consumer postPopulation Path srgZip = dependency.resolveFile().orElseThrow(() -> new RuntimeException("Could not resolve srg")).toPath(); try (FileSystem fs = FileSystems.newFileSystem(new URI("jar:" + srgZip.toUri()), ImmutableMap.of("create", false))) { - Files.copy(fs.getPath("config", "joined.tsrg"), srg, StandardCopyOption.REPLACE_EXISTING); + Path srgPath = fs.getPath("joined.srg"); + + if (Files.exists(srgPath)) { + try (Reader reader = Files.newBufferedReader(srgPath); Writer writer = Files.newBufferedWriter(srg)) { + new TSrgWriter(writer).write(new SrgReader(reader).read()); + } + } else { + Files.copy(fs.getPath("config", "joined.tsrg"), srg, StandardCopyOption.REPLACE_EXISTING); + } } } diff --git a/src/main/java/net/fabricmc/loom/configuration/providers/forge/fg2/MinecraftLegacyPatchedProvider.java b/src/main/java/net/fabricmc/loom/configuration/providers/forge/fg2/MinecraftLegacyPatchedProvider.java new file mode 100644 index 000000000..e87004c23 --- /dev/null +++ b/src/main/java/net/fabricmc/loom/configuration/providers/forge/fg2/MinecraftLegacyPatchedProvider.java @@ -0,0 +1,447 @@ +/* + * This file is part of fabric-loom, licensed under the MIT License (MIT). + * + * Copyright (c) 2021 FabricMC + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package net.fabricmc.loom.configuration.providers.forge.fg2; + +import java.io.BufferedReader; +import java.io.ByteArrayInputStream; +import java.io.File; +import java.io.IOException; +import java.io.InputStreamReader; +import java.net.URI; +import java.nio.file.FileSystem; +import java.nio.file.FileSystems; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.StandardCopyOption; +import java.nio.file.StandardOpenOption; +import java.util.Arrays; +import java.util.function.Consumer; +import java.util.function.Function; +import java.util.function.Predicate; +import java.util.stream.Stream; + +import com.google.common.base.Stopwatch; +import com.google.common.collect.ImmutableMap; +import net.md_5.specialsource.AccessChange; +import net.md_5.specialsource.AccessMap; +import org.gradle.api.Project; +import org.gradle.api.logging.Logger; +import org.objectweb.asm.AnnotationVisitor; +import org.objectweb.asm.ClassReader; +import org.objectweb.asm.ClassVisitor; +import org.objectweb.asm.ClassWriter; +import org.objectweb.asm.FieldVisitor; +import org.objectweb.asm.MethodVisitor; +import org.objectweb.asm.Opcodes; + +import net.fabricmc.loom.configuration.providers.MinecraftProviderImpl; +import net.fabricmc.loom.configuration.providers.forge.MinecraftPatchedProvider; +import net.fabricmc.loom.configuration.providers.forge.PatchProvider; +import net.fabricmc.loom.util.FileSystemUtil; +import net.fabricmc.loom.util.ThreadingUtils; +import net.fabricmc.loom.util.TinyRemapperHelper; +import net.fabricmc.loom.util.ZipUtils; +import net.fabricmc.mappingio.tree.MappingTree; +import net.fabricmc.stitch.merge.JarMerger; + +public class MinecraftLegacyPatchedProvider extends MinecraftPatchedProvider { + // Step 1: Binary Patch (global) + private File minecraftClientPatchedJar; + private File minecraftServerPatchedJar; + // Step 2: Merge (global) + private File minecraftMergedPatchedJar; + // Step 4: Access Transform (global or project) + private File minecraftMergedPatchedAtJar; + + private File forgeJar; + + public MinecraftLegacyPatchedProvider(Project project) { + super(project); + } + + @Override + public void initFiles() throws IOException { + filesDirty = false; + initAts(); + + File globalCache = getExtension().getForgeProvider().getGlobalCache(); + File projectDir = usesProjectCache() ? getExtension().getForgeProvider().getProjectCache() : globalCache; + projectDir.mkdirs(); + + minecraftClientPatchedJar = new File(globalCache, "client-patched.jar"); + minecraftServerPatchedJar = new File(globalCache, "server-patched.jar"); + minecraftMergedPatchedJar = new File(globalCache, "merged-patched.jar"); + minecraftMergedPatchedAtJar = new File(projectDir, "merged-at-patched.jar"); + + forgeJar = new File(globalCache, "forge.jar"); + + if (isRefreshDeps() || Stream.of(getGlobalCaches()).anyMatch(((Predicate) File::exists).negate()) + || !isPatchedJarUpToDate(forgeJar) || !isPatchedJarUpToDate(minecraftMergedPatchedAtJar)) { + cleanAllCache(); + } else if (atDirty || Stream.of(getProjectCache()).anyMatch(((Predicate) File::exists).negate())) { + cleanProjectCache(); + } + } + + @Override + protected File[] getGlobalCaches() { + return new File[] { + minecraftClientPatchedJar, + minecraftServerPatchedJar, + minecraftMergedPatchedJar, + forgeJar, + }; + } + + @Override + protected File[] getProjectCache() { + return new File[] { + minecraftMergedPatchedAtJar, + }; + } + + @Override + public void provide(DependencyInfo dependency, Consumer postPopulationScheduler) throws Exception { + initFiles(); + + if (atDirty) { + getProject().getLogger().lifecycle(":found dirty access transformers"); + } + } + + @Override + public void finishProvide() throws Exception { + if (!forgeJar.exists()) { + filesDirty = true; + patchForge(getProject().getLogger()); + applyLoomPatchVersion(forgeJar.toPath()); + } + + if (!minecraftClientPatchedJar.exists() || !minecraftServerPatchedJar.exists()) { + filesDirty = true; + patchJars(getProject().getLogger()); + } + + if (filesDirty || !minecraftMergedPatchedJar.exists()) { + filesDirty = true; + mergeJars(getProject().getLogger()); + } + + if (atDirty || filesDirty || !minecraftMergedPatchedAtJar.exists()) { + filesDirty = true; + accessTransformForge(getProject().getLogger()); + applyLoomPatchVersion(minecraftMergedPatchedAtJar.toPath()); + } + } + + private void patchForge(Logger logger) throws Exception { + Stopwatch stopwatch = Stopwatch.createStarted(); + logger.lifecycle(":patching forge"); + + Files.copy(getExtension().getForgeUniversalProvider().getForge().toPath(), forgeJar.toPath(), StandardCopyOption.REPLACE_EXISTING); + + // For the development environment, we need to remove the binpatches, otherwise forge will try to re-apply them + try (FileSystemUtil.Delegate fs = FileSystemUtil.getJarFileSystem(forgeJar, false)) { + Files.delete(fs.get().getPath("binpatches.pack.lzma")); + } + + // Older versions of Forge rely on utility classes from log4j-core 2.0-beta9 but we'll upgrade the runtime to a + // release version (so we can use the TerminalConsoleAppender) where some of those classes have been moved from + // a `helpers` to a `utils` package. + // To allow Forge to work regardless, we'll re-package those helper classes into the forge jar. + Path log4jBeta9 = Arrays.stream(TinyRemapperHelper.getMinecraftDependencies(getProject())) + .filter(it -> it.getFileName().toString().equals("log4j-core-2.0-beta9.jar")) + .findAny() + .orElse(null); + if (log4jBeta9 != null) { + Predicate isHelper = path -> path.startsWith("/org/apache/logging/log4j/core/helpers"); + walkFileSystems(log4jBeta9.toFile(), forgeJar, isHelper, this::copyReplacing); + } + + logger.lifecycle(":patched forge in " + stopwatch.stop()); + } + + private void patchJars(Logger logger) throws Exception { + Stopwatch stopwatch = Stopwatch.createStarted(); + logger.lifecycle(":patching jars"); + + MinecraftProviderImpl minecraftProvider = getExtension().getMinecraftProvider(); + PatchProvider patchProvider = getExtension().getPatchProvider(); + patchJars(minecraftProvider.minecraftServerJar, minecraftServerPatchedJar, patchProvider.serverPatches); + patchJars(minecraftProvider.minecraftClientJar, minecraftClientPatchedJar, patchProvider.clientPatches); + + logger.lifecycle(":patched jars in " + stopwatch.stop()); + } + + @Override + protected void patchJars(File clean, File output, Path patches) throws Exception { + super.patchJars(clean, output, patches); + + // Patching only preserves affected classes, everything else we need to copy manually + copyMissingClasses(clean, output); + copyNonClassFiles(clean, output); + + // Workaround Forge patches apparently violating the JVM spec (see ParameterAnnotationsFixer for details) + modifyClasses(output, ParameterAnnotationsFixer::new); + } + + private void mergeJars(Logger logger) throws Exception { + logger.info(":merging jars"); + Stopwatch stopwatch = Stopwatch.createStarted(); + + try (JarMerger jarMerger = new JarMerger(minecraftClientPatchedJar, minecraftServerPatchedJar, minecraftMergedPatchedJar)) { + jarMerger.enableSyntheticParamsOffset(); + jarMerger.merge(); + } + + // The JarMerger adds Sided annotations but so do the Forge patches. The latter doesn't require extra + // dependencies beyond Forge, so we'll keep those and remove the Fabric ones. + modifyClasses(minecraftMergedPatchedJar, FabricSideStripper::new); + + logger.info(":merged jars in " + stopwatch); + } + + private void accessTransformForge(Logger logger) throws Exception { + Stopwatch stopwatch = Stopwatch.createStarted(); + + logger.lifecycle(":access transforming minecraft"); + + MappingTree mappingTree = getExtension().getMappingsProvider().getMappingsWithSrg(); + + AccessMap accessMap = new LegacyAccessMap(); + + byte[] forgeAt = ZipUtils.unpack(forgeJar.toPath(), "forge_at.cfg"); + accessMap.loadAccessTransformer(new BufferedReader(new InputStreamReader(new ByteArrayInputStream(forgeAt)))); + + for (File projectAt : projectAts) { + accessMap.loadAccessTransformer(projectAt); + } + + Files.copy(minecraftMergedPatchedJar.toPath(), minecraftMergedPatchedAtJar.toPath(), StandardCopyOption.REPLACE_EXISTING); + + modifyClasses(minecraftMergedPatchedAtJar, writer -> new AccessTransformingVisitor(accessMap, mappingTree, writer)); + + logger.lifecycle(":access transformed minecraft in " + stopwatch.stop()); + } + + private void modifyClasses(File jarFile, Function func) throws Exception { + try (FileSystem fs = FileSystems.newFileSystem(new URI("jar:" + jarFile.toURI()), ImmutableMap.of("create", false))) { + ThreadingUtils.TaskCompleter completer = ThreadingUtils.taskCompleter(); + + for (Path file : (Iterable) Files.walk(fs.getPath("/"))::iterator) { + if (!file.toString().endsWith(".class")) continue; + + completer.add(() -> { + byte[] original = Files.readAllBytes(file); + + ClassReader reader = new ClassReader(original); + ClassWriter writer = new ClassWriter(reader, 0); + reader.accept(func.apply(writer), 0); + + byte[] modified = writer.toByteArray(); + + if (!Arrays.equals(original, modified)) { + Files.write(file, modified, StandardOpenOption.TRUNCATE_EXISTING); + } + }); + } + + completer.complete(); + } + } + + public File getMergedJar() { + return minecraftMergedPatchedAtJar; + } + + public File getForgeMergedJar() { + return forgeJar; + } + + /** + * It seems that Forge patches produce class files which are in violation of the JVM spec. Specifically, the value + * of Runtime[In]VisibleParameterAnnotation.num_parameters is by SE8 spec required to match the number of formal + * parameters of the method (and may be ignored in favor of directly looking at the method arguments, which is + * indeed what the OpenJDK 8 compiler does). Using a smaller value (possible if e.g. the last parameter has no + * annotations) will cause the compiler to read past the end of the table, throwing an exception and therefore being + * unable to read the class file. + *
+ * This class visitor fixes that by ignoring the original num_parameters value, letting the MethodVisitor compute a + * new value based on its signature. This will at first produce an invalid count when there are synthetic parameters + * but later, during mergeJars, those will be properly offset (enableSyntheticParamsOffset). + *
+ * SE8 JVM spec: https://docs.oracle.com/javase/specs/jvms/se8/html/jvms-4.html#jvms-4.7.18 + * Example method affected: RenderGlobal.ContainerLocalRenderInformation(RenderChunk, EnumFacing, int) + */ + private static class ParameterAnnotationsFixer extends ClassVisitor { + private ParameterAnnotationsFixer(ClassVisitor classVisitor) { + super(Opcodes.ASM9, classVisitor); + } + + @Override + public MethodVisitor visitMethod(int access, String name, String descriptor, String signature, String[] exceptions) { + MethodVisitor methodVisitor = super.visitMethod(access, name, descriptor, signature, exceptions); + + // This issue has so far only been observed with constructors, so we can skip everything else + if (name.equals("")) { + methodVisitor = new MethodFixer(methodVisitor); + } + + return methodVisitor; + } + + private static class MethodFixer extends MethodVisitor { + private MethodFixer(MethodVisitor methodVisitor) { + super(Opcodes.ASM9, methodVisitor); + } + + @Override + public void visitAnnotableParameterCount(int parameterCount, boolean visible) { + // Not calling visitAnnotableParameterCount will cause it to compute its value from the method signature + } + } + } + + private static class FabricSideStripper extends ClassVisitor { + private static final String SIDED_DESCRIPTOR = "Lnet/fabricmc/api/Environment;"; + + private FabricSideStripper(ClassVisitor classVisitor) { + super(Opcodes.ASM9, classVisitor); + } + + @Override + public AnnotationVisitor visitAnnotation(String descriptor, boolean visible) { + if (descriptor.equals(SIDED_DESCRIPTOR)) { + return null; + } + + return super.visitAnnotation(descriptor, visible); + } + + @Override + public FieldVisitor visitField(int access, String name, String descriptor, String signature, Object value) { + return new FieldStripper(super.visitField(access, name, descriptor, signature, value)); + } + + @Override + public MethodVisitor visitMethod(int access, String name, String descriptor, String signature, String[] exceptions) { + return new MethodStripper(super.visitMethod(access, name, descriptor, signature, exceptions)); + } + + private static class FieldStripper extends FieldVisitor { + private FieldStripper(FieldVisitor fieldVisitor) { + super(Opcodes.ASM9, fieldVisitor); + } + + @Override + public AnnotationVisitor visitAnnotation(String descriptor, boolean visible) { + if (descriptor.equals(SIDED_DESCRIPTOR)) { + return null; + } + + return super.visitAnnotation(descriptor, visible); + } + } + + private static class MethodStripper extends MethodVisitor { + private MethodStripper(MethodVisitor methodVisitor) { + super(Opcodes.ASM9, methodVisitor); + } + + @Override + public AnnotationVisitor visitAnnotation(String descriptor, boolean visible) { + if (descriptor.equals(SIDED_DESCRIPTOR)) { + return null; + } + + return super.visitAnnotation(descriptor, visible); + } + } + } + + private static class LegacyAccessMap extends AccessMap { + @Override + public void addAccessChange(String key, AccessChange accessChange) { + // Forge's AT separates fields/methods from their owner by a space but we require a slash + int spaceIdx = key.indexOf(' '); + + if (spaceIdx != -1 && key.charAt(spaceIdx + 1) != '(') { + key = key.replaceFirst(" ", "/"); + } + + super.addAccessChange(key, accessChange); + } + } + + private static class AccessTransformingVisitor extends ClassVisitor { + private final AccessMap accessMap; + private final MappingTree mappingTree; + private final int src; + private final int dst; + + private MappingTree.ClassMapping classMapping; + private String mappedClassName; + + private AccessTransformingVisitor(AccessMap accessMap, MappingTree mappingTree, ClassVisitor classVisitor) { + super(Opcodes.ASM9, classVisitor); + this.accessMap = accessMap; + this.mappingTree = mappingTree; + this.src = mappingTree.getNamespaceId("official"); + this.dst = mappingTree.getNamespaceId("srg"); + } + + @Override + public void visit(int version, int access, String name, String signature, String superName, String[] interfaces) { + classMapping = mappingTree.getClass(name, src); + mappedClassName = classMapping != null ? classMapping.getName(dst) : name; + access = accessMap.applyClassAccess(mappedClassName, access); + super.visit(version, access, name, signature, superName, interfaces); + } + + @Override + public FieldVisitor visitField(int access, String name, String descriptor, String signature, Object value) { + MappingTree.FieldMapping field = classMapping != null ? classMapping.getField(name, descriptor, src) : null; + String mappedName = field != null ? field.getName(dst) : name; + access = accessMap.applyFieldAccess(mappedClassName, mappedName, access); + return super.visitField(access, name, descriptor, signature, value); + } + + @Override + public MethodVisitor visitMethod(int access, String name, String desc, String signature, String[] exceptions) { + MappingTree.MethodMapping method = classMapping != null ? classMapping.getMethod(name, desc, src) : null; + String mappedName = method != null ? method.getName(dst) : name; + String mappedDesc = method != null ? method.getDesc(dst) : mappingTree.mapDesc(desc, src, dst); + access = accessMap.applyMethodAccess(mappedClassName, mappedName, mappedDesc, access); + return super.visitMethod(access, name, desc, signature, exceptions); + } + + @Override + public void visitInnerClass(String name, String outerName, String innerName, int access) { + MappingTree.ClassMapping classMapping = mappingTree.getClass(name, src); + access = accessMap.applyClassAccess(classMapping != null ? classMapping.getName(dst) : name, access); + super.visitInnerClass(name, outerName, innerName, access); + } + } +} diff --git a/src/main/java/net/fabricmc/loom/configuration/providers/forge/fg2/Pack200Provider.java b/src/main/java/net/fabricmc/loom/configuration/providers/forge/fg2/Pack200Provider.java new file mode 100644 index 000000000..8912c42cf --- /dev/null +++ b/src/main/java/net/fabricmc/loom/configuration/providers/forge/fg2/Pack200Provider.java @@ -0,0 +1,8 @@ +package net.fabricmc.loom.configuration.providers.forge.fg2; + +import java.io.InputStream; +import java.util.jar.JarOutputStream; + +public interface Pack200Provider { + void unpack(InputStream inputStream, JarOutputStream outputStream); +} diff --git a/src/main/java/net/fabricmc/loom/configuration/providers/mappings/MappingsProviderImpl.java b/src/main/java/net/fabricmc/loom/configuration/providers/mappings/MappingsProviderImpl.java index 0f4614ce5..344071f00 100644 --- a/src/main/java/net/fabricmc/loom/configuration/providers/mappings/MappingsProviderImpl.java +++ b/src/main/java/net/fabricmc/loom/configuration/providers/mappings/MappingsProviderImpl.java @@ -52,6 +52,7 @@ import org.apache.tools.ant.util.StringUtils; import org.gradle.api.Project; import org.gradle.api.artifacts.Configuration; +import org.gradle.api.logging.Logger; import org.jetbrains.annotations.Nullable; import org.objectweb.asm.Opcodes; @@ -66,15 +67,18 @@ import net.fabricmc.loom.configuration.providers.MinecraftProviderImpl; import net.fabricmc.loom.configuration.providers.forge.MinecraftPatchedProvider; import net.fabricmc.loom.configuration.providers.forge.SrgProvider; +import net.fabricmc.loom.configuration.providers.forge.fg2.MinecraftLegacyPatchedProvider; import net.fabricmc.loom.configuration.providers.minecraft.MinecraftMappedProvider; import net.fabricmc.loom.util.Constants; import net.fabricmc.loom.util.DeletingFileVisitor; import net.fabricmc.loom.util.DownloadUtil; +import net.fabricmc.loom.util.LoggerFilter; import net.fabricmc.loom.util.ZipUtils; import net.fabricmc.loom.util.srg.MCPReader; import net.fabricmc.loom.util.srg.SrgMerger; import net.fabricmc.loom.util.srg.SrgNamedWriter; import net.fabricmc.mappingio.MappingReader; +import net.fabricmc.mappingio.MappingWriter; import net.fabricmc.mappingio.adapter.MappingNsCompleter; import net.fabricmc.mappingio.adapter.MappingSourceNsSwitch; import net.fabricmc.mappingio.format.MappingFormat; @@ -83,6 +87,7 @@ import net.fabricmc.mappingio.tree.MappingTree; import net.fabricmc.mappingio.tree.MemoryMappingTree; import net.fabricmc.stitch.Command; +import net.fabricmc.stitch.commands.CommandGenerateIntermediary; import net.fabricmc.stitch.commands.CommandProposeFieldNames; import net.fabricmc.stitch.commands.tinyv2.TinyFile; import net.fabricmc.stitch.commands.tinyv2.TinyV2Writer; @@ -147,7 +152,12 @@ public void provide(DependencyInfo dependency, Consumer postPopulation } if (getExtension().isForge()) { - patchedProvider = new MinecraftPatchedProvider(getProject()); + if (getExtension().isLegacyForge()) { + patchedProvider = new MinecraftLegacyPatchedProvider(getProject()); + } else { + patchedProvider = new MinecraftPatchedProvider(getProject()); + } + patchedProvider.provide(dependency, postPopulationScheduler); } @@ -157,7 +167,7 @@ public void provide(DependencyInfo dependency, Consumer postPopulation if (getExtension().shouldGenerateSrgTiny()) { if (Files.notExists(tinyMappingsWithSrg) || isRefreshDeps()) { // Merge tiny mappings with srg - SrgMerger.mergeSrg(getProject().getLogger(), getExtension().getMappingsProvider()::getMojmapSrgFileIfPossible, getRawSrgFile(), tinyMappings, tinyMappingsWithSrg, true); + SrgMerger.mergeSrg(getProject().getLogger(), getExtension().getMappingsProvider()::getMojmapSrgFileIfPossible, getRawSrgFile(), tinyMappings, tinyMappingsWithSrg, true, getExtension().isLegacyForge()); } mappingTreeWithSrg = readMappings(tinyMappingsWithSrg); @@ -578,17 +588,48 @@ public Path getIntermediaryTiny() throws IOException { hasRefreshed = true; // Download and extract intermediary - String encodedMinecraftVersion = UrlEscapers.urlFragmentEscaper().escape(getMinecraftProvider().minecraftVersion()); - String intermediaryArtifactUrl = getExtension().getIntermediaryUrl(encodedMinecraftVersion); - File intermediaryJar = getMinecraftProvider().file("intermediary-v2.jar"); - DownloadUtil.downloadIfChanged(new URL(intermediaryArtifactUrl), intermediaryJar, getProject().getLogger()); - extractMappings(intermediaryJar.toPath(), intermediaryTiny); + if (getExtension().isLegacyForge()) { + Path intermediaryV1Tiny = getMinecraftProvider().file("intermediary-v1.tiny").toPath(); + generateDummyIntermediary(getProject().getLogger(), intermediaryV1Tiny, intermediaryTiny); + } else { + String encodedMinecraftVersion = UrlEscapers.urlFragmentEscaper().escape(getMinecraftProvider().minecraftVersion()); + String intermediaryArtifactUrl = getExtension().getIntermediaryUrl(encodedMinecraftVersion); + File intermediaryJar = getMinecraftProvider().file("intermediary-v2.jar"); + DownloadUtil.downloadIfChanged(new URL(intermediaryArtifactUrl), intermediaryJar, getProject().getLogger()); + extractMappings(intermediaryJar.toPath(), intermediaryTiny); + } } } return intermediaryTiny; } + private void generateDummyIntermediary(Logger logger, Path tinyV1, Path tinyV2) throws IOException { + Stopwatch stopwatch = Stopwatch.createStarted(); + logger.lifecycle(":generating dummy intermediary"); + + Path minecraftJar = getExtension().getMinecraftProvider().getMergedJar().toPath(); + + Files.deleteIfExists(tinyV1); // file must not exist, otherwise stitch will try to read it + + CommandGenerateIntermediary command = new CommandGenerateIntermediary(); + LoggerFilter.withSystemOutAndErrSuppressed(() -> { + try { + command.run(new String[]{ minecraftJar.toAbsolutePath().toString(), tinyV1.toAbsolutePath().toString() }); + } catch (IOException e) { + throw e; + } catch (Exception e) { + throw new IOException("Failed to generate intermediary", e); + } + }); + + try (MappingWriter writer = MappingWriter.create(tinyV2, MappingFormat.TINY_2)) { + MappingReader.read(tinyV1, writer); + } + + logger.lifecycle(":generated dummy intermediary in " + stopwatch.stop()); + } + @Override public Path mappingsWorkingDir() { return mappingsWorkingDir; diff --git a/src/main/java/net/fabricmc/loom/configuration/providers/minecraft/MinecraftMappedProvider.java b/src/main/java/net/fabricmc/loom/configuration/providers/minecraft/MinecraftMappedProvider.java index ec0039958..a137aa8b7 100644 --- a/src/main/java/net/fabricmc/loom/configuration/providers/minecraft/MinecraftMappedProvider.java +++ b/src/main/java/net/fabricmc/loom/configuration/providers/minecraft/MinecraftMappedProvider.java @@ -266,7 +266,17 @@ public Info(Path assets, Path input, Path outputMapped, Path outputIntermediary, } public void remap(TinyRemapper remapper, Mutable mappings, List postApply, Info vanilla, @Nullable Info forge, String fromM) throws IOException { - Set classNames = getExtension().isForge() ? InnerClassRemapper.readClassNames(vanilla.input) : null; + Set classNames; + + if (getExtension().isForge()) { + classNames = InnerClassRemapper.readClassNames(vanilla.input); + + if (forge != null) { + classNames.addAll(InnerClassRemapper.readClassNames(forge.input)); + } + } else { + classNames = null; + } for (String toM : getExtension().isForge() ? Arrays.asList(MappingsNamespace.INTERMEDIARY.toString(), MappingsNamespace.SRG.toString(), MappingsNamespace.NAMED.toString()) : Arrays.asList(MappingsNamespace.INTERMEDIARY.toString(), MappingsNamespace.NAMED.toString())) { Path output = MappingsNamespace.NAMED.toString().equals(toM) ? vanilla.outputMapped : MappingsNamespace.SRG.toString().equals(toM) ? vanilla.outputSrg : vanilla.outputIntermediary; diff --git a/src/main/java/net/fabricmc/loom/extension/ForgeExtensionImpl.java b/src/main/java/net/fabricmc/loom/extension/ForgeExtensionImpl.java index 25da6717a..5a4956d95 100644 --- a/src/main/java/net/fabricmc/loom/extension/ForgeExtensionImpl.java +++ b/src/main/java/net/fabricmc/loom/extension/ForgeExtensionImpl.java @@ -42,6 +42,7 @@ import net.fabricmc.loom.api.ForgeExtensionAPI; import net.fabricmc.loom.api.ForgeLocalMod; import net.fabricmc.loom.configuration.ide.RunConfigSettings; +import net.fabricmc.loom.configuration.providers.forge.fg2.Pack200Provider; public class ForgeExtensionImpl implements ForgeExtensionAPI { private final LoomGradleExtension extension; @@ -52,6 +53,7 @@ public class ForgeExtensionImpl implements ForgeExtensionAPI { private final Property useCustomMixin; private final List dataGenMods = new ArrayList<>(); // not a property because it has custom adding logic private final NamedDomainObjectContainer localMods; + private final Property pack200Provider; @Inject public ForgeExtensionImpl(Project project, LoomGradleExtension extension) { @@ -63,6 +65,7 @@ public ForgeExtensionImpl(Project project, LoomGradleExtension extension) { useCustomMixin = project.getObjects().property(Boolean.class).convention(true); localMods = project.container(ForgeLocalMod.class, baseName -> new ForgeLocalMod(project, baseName, new ArrayList<>())); + pack200Provider = project.getObjects().property(Pack200Provider.class); // Create default mod from main source set localMods(mod -> mod.create("main").add("main")); @@ -133,4 +136,9 @@ public void localMods(Action> action) public NamedDomainObjectContainer getLocalMods() { return localMods; } + + @Override + public Property getPack200Provider() { + return pack200Provider; + } } diff --git a/src/main/java/net/fabricmc/loom/util/Constants.java b/src/main/java/net/fabricmc/loom/util/Constants.java index 8a68b0fb5..2ce96c46d 100644 --- a/src/main/java/net/fabricmc/loom/util/Constants.java +++ b/src/main/java/net/fabricmc/loom/util/Constants.java @@ -177,4 +177,10 @@ public static final class Forge { private Forge() { } } + + public static final class LegacyForge { + public static final String LAUNCH_WRAPPER = "net.minecraft.launchwrapper.Launch"; + public static final String FML_TWEAKER = "net.minecraftforge.fml.common.launcher.FMLTweaker"; + public static final String FML_SERVER_TWEAKER = "net.minecraftforge.fml.common.launcher.FMLServerTweaker"; + } } diff --git a/src/main/java/net/fabricmc/loom/util/LoggerFilter.java b/src/main/java/net/fabricmc/loom/util/LoggerFilter.java index 1f9f9b96a..712172dc2 100644 --- a/src/main/java/net/fabricmc/loom/util/LoggerFilter.java +++ b/src/main/java/net/fabricmc/loom/util/LoggerFilter.java @@ -26,6 +26,7 @@ import java.io.PrintStream; +import org.apache.commons.io.output.NullOutputStream; import org.jetbrains.annotations.NotNull; public class LoggerFilter { @@ -46,4 +47,31 @@ public PrintStream printf(@NotNull String format, Object... args) { // Failed to replace logger filter, just ignore } } + + public static void withSystemOutAndErrSuppressed(CheckedRunnable block) throws T { + PrintStream previousOut = System.out; + PrintStream previousErr = System.err; + + try { + System.setOut(new PrintStream(NullOutputStream.NULL_OUTPUT_STREAM)); + System.setErr(new PrintStream(NullOutputStream.NULL_OUTPUT_STREAM)); + } catch (SecurityException ignored) { + // Failed to replace logger, just ignore + } + + try { + block.run(); + } finally { + try { + System.setOut(previousOut); + System.setErr(previousErr); + } catch (SecurityException ignored) { + // Failed to replace logger, just ignore + } + } + } + + public interface CheckedRunnable { + void run() throws T; + } } diff --git a/src/main/java/net/fabricmc/loom/util/srg/SrgMerger.java b/src/main/java/net/fabricmc/loom/util/srg/SrgMerger.java index ede2dbad8..93125b3db 100644 --- a/src/main/java/net/fabricmc/loom/util/srg/SrgMerger.java +++ b/src/main/java/net/fabricmc/loom/util/srg/SrgMerger.java @@ -72,15 +72,17 @@ public final class SrgMerger { private final FlatMappingVisitor flatOutput; private final List postProcesses = new ArrayList<>(); private final boolean lenient; + private final boolean legacy; private final Set methodSrgNames = new HashSet<>(); - public SrgMerger(Logger logger, Path srg, @Nullable Supplier mojmap, Path tiny, boolean lenient) throws IOException { + public SrgMerger(Logger logger, Path srg, @Nullable Supplier mojmap, Path tiny, boolean lenient, boolean legacy) throws IOException { this.logger = logger; this.srg = readSrg(srg, mojmap); this.src = new MemoryMappingTree(); this.output = new MemoryMappingTree(); this.flatOutput = new RegularAsFlatMappingVisitor(output); this.lenient = lenient; + this.legacy = legacy; MappingReader.read(tiny, this.src); @@ -115,14 +117,15 @@ public MemoryMappingTree merge() throws IOException { * @param tiny the tiny file * @param out the output file, will be in tiny v2 * @param lenient whether to ignore missing tiny mapping + * @param legacy treat any method as mapped, even when it is lacking the 'func_' prefix * @throws IOException if an IO error occurs while reading or writing the mappings * @throws MappingException if the input tiny tree's default namespace is not 'official' * or if an element mentioned in the SRG file does not have tiny mappings */ - public static void mergeSrg(Logger logger, @Nullable Supplier mojmap, Path srg, Path tiny, Path out, boolean lenient) + public static void mergeSrg(Logger logger, @Nullable Supplier mojmap, Path srg, Path tiny, Path out, boolean lenient, boolean legacy) throws IOException, MappingException { Stopwatch stopwatch = Stopwatch.createStarted(); - MemoryMappingTree tree = mergeSrg(logger, mojmap, srg, tiny, lenient); + MemoryMappingTree tree = mergeSrg(logger, mojmap, srg, tiny, lenient, legacy); try (Tiny2Writer writer = new Tiny2Writer(Files.newBufferedWriter(out), false)) { tree.accept(writer); @@ -140,14 +143,15 @@ public static void mergeSrg(Logger logger, @Nullable Supplier mojmap, Path * @param mojmap the path to the mojmap file used for generating mojmap+srg names, may be null * @param tiny the tiny file * @param lenient whether to ignore missing tiny mapping + * @param legacy treat any method as mapped, even when it is lacking the 'func_' prefix * @return the created mapping tree * @throws IOException if an IO error occurs while reading or writing the mappings * @throws MappingException if the input tiny tree's default namespace is not 'official' * or if an element mentioned in the SRG file does not have tiny mappings */ - public static MemoryMappingTree mergeSrg(Logger logger, @Nullable Supplier mojmap, Path srg, Path tiny, boolean lenient) + public static MemoryMappingTree mergeSrg(Logger logger, @Nullable Supplier mojmap, Path srg, Path tiny, boolean lenient, boolean legacy) throws IOException, MappingException { - return new SrgMerger(logger, srg, mojmap, tiny, lenient).merge(); + return new SrgMerger(logger, srg, mojmap, tiny, lenient, legacy).merge(); } private MemoryMappingTree readSrg(Path srg, @Nullable Supplier mojmap) throws IOException { @@ -256,7 +260,7 @@ private void classToTiny(MappingTree.ClassMapping klass) throws IOException { methodToTiny(obf, method, methodSrgName, def); - if (methodSrgName.startsWith("func_") || methodSrgName.startsWith("m_")) { + if (methodSrgName.startsWith("func_") || methodSrgName.startsWith("m_") || legacy) { methodSrgNames.add(methodSrgName); } } diff --git a/src/main/java/net/fabricmc/loom/util/srg/SrgNamedWriter.java b/src/main/java/net/fabricmc/loom/util/srg/SrgNamedWriter.java index 456b8b1bb..f999f936f 100644 --- a/src/main/java/net/fabricmc/loom/util/srg/SrgNamedWriter.java +++ b/src/main/java/net/fabricmc/loom/util/srg/SrgNamedWriter.java @@ -28,7 +28,14 @@ import java.nio.file.Files; import java.nio.file.Path; +import org.cadixdev.lorenz.MappingSet; +import org.cadixdev.lorenz.impl.MappingSetModelFactoryImpl; +import org.cadixdev.lorenz.impl.model.InnerClassMappingImpl; +import org.cadixdev.lorenz.impl.model.TopLevelClassMappingImpl; import org.cadixdev.lorenz.io.srg.SrgWriter; +import org.cadixdev.lorenz.model.ClassMapping; +import org.cadixdev.lorenz.model.InnerClassMapping; +import org.cadixdev.lorenz.model.TopLevelClassMapping; import org.gradle.api.logging.Logger; import net.fabricmc.lorenztiny.TinyMappingsReader; @@ -40,8 +47,35 @@ public static void writeTo(Logger logger, Path srgFile, MappingTree mappings, St try (SrgWriter writer = new SrgWriter(Files.newBufferedWriter(srgFile))) { try (TinyMappingsReader reader = new TinyMappingsReader(mappings, from, to)) { - writer.write(reader.read()); + writer.write(reader.read(MappingSet.create(new ClassesAlwaysHaveDeobfNameFactory()))); } } } + + /** + * Legacy Forge's FMLDeobfuscatingRemapper requires class mappings, even if they are identity maps, but such + * mappings are filtered out by the SrgWriter. To get around that, we create a custom mapping set which always + * claims to have deobfuscated names set for classes. + */ + private static class ClassesAlwaysHaveDeobfNameFactory extends MappingSetModelFactoryImpl { + @Override + public TopLevelClassMapping createTopLevelClassMapping(MappingSet parent, String obfuscatedName, String deobfuscatedName) { + return new TopLevelClassMappingImpl(parent, obfuscatedName, deobfuscatedName) { + @Override + public boolean hasDeobfuscatedName() { + return true; + } + }; + } + + @Override + public InnerClassMapping createInnerClassMapping(ClassMapping parent, String obfuscatedName, String deobfuscatedName) { + return new InnerClassMappingImpl(parent, obfuscatedName, deobfuscatedName) { + @Override + public boolean hasDeobfuscatedName() { + return true; + } + }; + } + } } diff --git a/src/test/groovy/net/fabricmc/loom/test/integration/forge/ForgeTest.groovy b/src/test/groovy/net/fabricmc/loom/test/integration/forge/ForgeTest.groovy index 28dfde1b5..1b4ca4b46 100644 --- a/src/test/groovy/net/fabricmc/loom/test/integration/forge/ForgeTest.groovy +++ b/src/test/groovy/net/fabricmc/loom/test/integration/forge/ForgeTest.groovy @@ -54,5 +54,7 @@ class ForgeTest extends Specification implements GradleProjectTestTrait { '1.16.5' | "36.2.4" | "\"net.fabricmc:yarn:1.16.5+build.5:v2\"" '1.14.4' | "28.2.23" | "loom.officialMojangMappings()" '1.14.4' | "28.2.23" | "\"net.fabricmc:yarn:1.14.4+build.18:v2\"" + '1.12.2' | "14.23.0.2486" | "\"de.oceanlabs.mcp:mcp_snapshot:20170615-1.12\"" + '1.8.9' | "11.15.1.2318-1.8.9" | "\"de.oceanlabs.mcp:mcp_stable:22-1.8.9\"" } } diff --git a/src/test/resources/projects/forge/simple/build.gradle b/src/test/resources/projects/forge/simple/build.gradle index dcee84dc3..2e1d75828 100644 --- a/src/test/resources/projects/forge/simple/build.gradle +++ b/src/test/resources/projects/forge/simple/build.gradle @@ -57,6 +57,13 @@ jar { } } +loom { + forge { + // FIXME should eventually be handled by architectury-pack200 gradle plugin? + pack200Provider = new dev.architectury.pack200.java.Pack200Adapter() + } +} + // configure the maven publication publishing { publications { From 0ff81f4cc221d47f28e7ace0417919541fb72414 Mon Sep 17 00:00:00 2001 From: Jonas Herzig Date: Mon, 10 Jan 2022 15:43:09 +0100 Subject: [PATCH 05/44] Clean up generated tiny v1 file after converting to v2 We never use the v1 file, so there isn't really any reason to keep it around. --- .../providers/mappings/MappingsProviderImpl.java | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/src/main/java/net/fabricmc/loom/configuration/providers/mappings/MappingsProviderImpl.java b/src/main/java/net/fabricmc/loom/configuration/providers/mappings/MappingsProviderImpl.java index 344071f00..1d5ebb693 100644 --- a/src/main/java/net/fabricmc/loom/configuration/providers/mappings/MappingsProviderImpl.java +++ b/src/main/java/net/fabricmc/loom/configuration/providers/mappings/MappingsProviderImpl.java @@ -589,8 +589,7 @@ public Path getIntermediaryTiny() throws IOException { // Download and extract intermediary if (getExtension().isLegacyForge()) { - Path intermediaryV1Tiny = getMinecraftProvider().file("intermediary-v1.tiny").toPath(); - generateDummyIntermediary(getProject().getLogger(), intermediaryV1Tiny, intermediaryTiny); + generateDummyIntermediary(getProject().getLogger(), intermediaryTiny); } else { String encodedMinecraftVersion = UrlEscapers.urlFragmentEscaper().escape(getMinecraftProvider().minecraftVersion()); String intermediaryArtifactUrl = getExtension().getIntermediaryUrl(encodedMinecraftVersion); @@ -604,13 +603,16 @@ public Path getIntermediaryTiny() throws IOException { return intermediaryTiny; } - private void generateDummyIntermediary(Logger logger, Path tinyV1, Path tinyV2) throws IOException { + private void generateDummyIntermediary(Logger logger, Path tinyV2) throws IOException { Stopwatch stopwatch = Stopwatch.createStarted(); logger.lifecycle(":generating dummy intermediary"); Path minecraftJar = getExtension().getMinecraftProvider().getMergedJar().toPath(); - Files.deleteIfExists(tinyV1); // file must not exist, otherwise stitch will try to read it + // create a temporary folder into which stitch will output the v1 file + // we cannot just create a temporary file directly, cause stitch will try to read it if it exists + Path tmpFolder = Files.createTempDirectory("dummy-intermediary"); + Path tinyV1 = tmpFolder.resolve("intermediary-v1.tiny"); CommandGenerateIntermediary command = new CommandGenerateIntermediary(); LoggerFilter.withSystemOutAndErrSuppressed(() -> { @@ -627,6 +629,9 @@ private void generateDummyIntermediary(Logger logger, Path tinyV1, Path tinyV2) MappingReader.read(tinyV1, writer); } + Files.delete(tinyV1); + Files.delete(tmpFolder); + logger.lifecycle(":generated dummy intermediary in " + stopwatch.stop()); } From c009507d447319d7e702a244e6a0ff33f08e7192 Mon Sep 17 00:00:00 2001 From: Jonas Herzig Date: Mon, 10 Jan 2022 15:44:37 +0100 Subject: [PATCH 06/44] Generate identity mappings in srg file only for legacy forge Cause modern versions of Forge do not need them. --- .../providers/mappings/MappingsProviderImpl.java | 2 +- .../java/net/fabricmc/loom/util/srg/SrgNamedWriter.java | 8 +++++--- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/src/main/java/net/fabricmc/loom/configuration/providers/mappings/MappingsProviderImpl.java b/src/main/java/net/fabricmc/loom/configuration/providers/mappings/MappingsProviderImpl.java index 1d5ebb693..51cc4ef41 100644 --- a/src/main/java/net/fabricmc/loom/configuration/providers/mappings/MappingsProviderImpl.java +++ b/src/main/java/net/fabricmc/loom/configuration/providers/mappings/MappingsProviderImpl.java @@ -202,7 +202,7 @@ public void provide(DependencyInfo dependency, Consumer postPopulation } if (Files.notExists(srgToNamedSrg) || isRefreshDeps()) { - SrgNamedWriter.writeTo(getProject().getLogger(), srgToNamedSrg, getMappingsWithSrg(), "srg", "named"); + SrgNamedWriter.writeTo(srgToNamedSrg, getMappingsWithSrg(), "srg", "named", getExtension().isLegacyForge()); } } diff --git a/src/main/java/net/fabricmc/loom/util/srg/SrgNamedWriter.java b/src/main/java/net/fabricmc/loom/util/srg/SrgNamedWriter.java index f999f936f..d7653297c 100644 --- a/src/main/java/net/fabricmc/loom/util/srg/SrgNamedWriter.java +++ b/src/main/java/net/fabricmc/loom/util/srg/SrgNamedWriter.java @@ -36,18 +36,20 @@ import org.cadixdev.lorenz.model.ClassMapping; import org.cadixdev.lorenz.model.InnerClassMapping; import org.cadixdev.lorenz.model.TopLevelClassMapping; -import org.gradle.api.logging.Logger; import net.fabricmc.lorenztiny.TinyMappingsReader; import net.fabricmc.mappingio.tree.MappingTree; public class SrgNamedWriter { - public static void writeTo(Logger logger, Path srgFile, MappingTree mappings, String from, String to) throws IOException { + public static void writeTo(Path srgFile, MappingTree mappings, String from, String to, boolean includeIdentityMappings) throws IOException { Files.deleteIfExists(srgFile); try (SrgWriter writer = new SrgWriter(Files.newBufferedWriter(srgFile))) { try (TinyMappingsReader reader = new TinyMappingsReader(mappings, from, to)) { - writer.write(reader.read(MappingSet.create(new ClassesAlwaysHaveDeobfNameFactory()))); + MappingSet mappingSet = includeIdentityMappings + ? MappingSet.create(new ClassesAlwaysHaveDeobfNameFactory()) + : MappingSet.create(); + writer.write(reader.read(mappingSet)); } } } From 6a2a900535f4c3452000ff8a12a384146da27ccc Mon Sep 17 00:00:00 2001 From: Jonas Herzig Date: Tue, 11 Jan 2022 16:15:26 +0100 Subject: [PATCH 07/44] Do not depend on lorenz impl classes to write identity mappings --- .../loom/util/srg/SrgNamedWriter.java | 48 ++++++++----------- 1 file changed, 19 insertions(+), 29 deletions(-) diff --git a/src/main/java/net/fabricmc/loom/util/srg/SrgNamedWriter.java b/src/main/java/net/fabricmc/loom/util/srg/SrgNamedWriter.java index d7653297c..cf795448c 100644 --- a/src/main/java/net/fabricmc/loom/util/srg/SrgNamedWriter.java +++ b/src/main/java/net/fabricmc/loom/util/srg/SrgNamedWriter.java @@ -25,17 +25,12 @@ package net.fabricmc.loom.util.srg; import java.io.IOException; +import java.io.Writer; import java.nio.file.Files; import java.nio.file.Path; import org.cadixdev.lorenz.MappingSet; -import org.cadixdev.lorenz.impl.MappingSetModelFactoryImpl; -import org.cadixdev.lorenz.impl.model.InnerClassMappingImpl; -import org.cadixdev.lorenz.impl.model.TopLevelClassMappingImpl; import org.cadixdev.lorenz.io.srg.SrgWriter; -import org.cadixdev.lorenz.model.ClassMapping; -import org.cadixdev.lorenz.model.InnerClassMapping; -import org.cadixdev.lorenz.model.TopLevelClassMapping; import net.fabricmc.lorenztiny.TinyMappingsReader; import net.fabricmc.mappingio.tree.MappingTree; @@ -44,40 +39,35 @@ public class SrgNamedWriter { public static void writeTo(Path srgFile, MappingTree mappings, String from, String to, boolean includeIdentityMappings) throws IOException { Files.deleteIfExists(srgFile); - try (SrgWriter writer = new SrgWriter(Files.newBufferedWriter(srgFile))) { + try (SrgWriter writer = newSrgWriter(Files.newBufferedWriter(srgFile), includeIdentityMappings)) { try (TinyMappingsReader reader = new TinyMappingsReader(mappings, from, to)) { - MappingSet mappingSet = includeIdentityMappings - ? MappingSet.create(new ClassesAlwaysHaveDeobfNameFactory()) - : MappingSet.create(); - writer.write(reader.read(mappingSet)); + writer.write(reader.read()); } } } + private static SrgWriter newSrgWriter(Writer writer, boolean includeIdentityMappings) { + return includeIdentityMappings ? new SrgWithIdentitiesWriter(writer) : new SrgWriter(writer); + } + /** * Legacy Forge's FMLDeobfuscatingRemapper requires class mappings, even if they are identity maps, but such - * mappings are filtered out by the SrgWriter. To get around that, we create a custom mapping set which always - * claims to have deobfuscated names set for classes. + * mappings are filtered out by the SrgWriter. To get around that, this SrgWriter manually emits identity mappings + * before emitting all regular mappings. */ - private static class ClassesAlwaysHaveDeobfNameFactory extends MappingSetModelFactoryImpl { - @Override - public TopLevelClassMapping createTopLevelClassMapping(MappingSet parent, String obfuscatedName, String deobfuscatedName) { - return new TopLevelClassMappingImpl(parent, obfuscatedName, deobfuscatedName) { - @Override - public boolean hasDeobfuscatedName() { - return true; - } - }; + private static class SrgWithIdentitiesWriter extends SrgWriter { + private SrgWithIdentitiesWriter(Writer writer) { + super(writer); } @Override - public InnerClassMapping createInnerClassMapping(ClassMapping parent, String obfuscatedName, String deobfuscatedName) { - return new InnerClassMappingImpl(parent, obfuscatedName, deobfuscatedName) { - @Override - public boolean hasDeobfuscatedName() { - return true; - } - }; + public void write(MappingSet mappings) { + mappings.getTopLevelClassMappings().stream() + .filter(cls -> !cls.hasDeobfuscatedName()) + .sorted(getConfig().getClassMappingComparator()) + .forEach(cls -> writer.format("CL: %s %s%n", cls.getFullObfuscatedName(), cls.getFullDeobfuscatedName())); + + super.write(mappings); } } } From 98173e3874d59d98bdb05298a6bf8e85d2502de8 Mon Sep 17 00:00:00 2001 From: Jonas Herzig Date: Tue, 11 Jan 2022 16:25:07 +0100 Subject: [PATCH 08/44] Use explicit if to separate modern and legacy runs setup --- .../providers/forge/ForgeUserdevProvider.java | 22 ++++++++++++------- 1 file changed, 14 insertions(+), 8 deletions(-) diff --git a/src/main/java/net/fabricmc/loom/configuration/providers/forge/ForgeUserdevProvider.java b/src/main/java/net/fabricmc/loom/configuration/providers/forge/ForgeUserdevProvider.java index ec98da784..b30e78f20 100644 --- a/src/main/java/net/fabricmc/loom/configuration/providers/forge/ForgeUserdevProvider.java +++ b/src/main/java/net/fabricmc/loom/configuration/providers/forge/ForgeUserdevProvider.java @@ -164,8 +164,14 @@ public void provide(DependencyInfo dependency, Consumer postPopulation // TODO: Should I copy the patches from here as well? // That'd require me to run the "MCP environment" fully up to merging. - JsonObject runs = isLegacyForge ? new JsonObject() : json.getAsJsonObject("runs"); + if (!isLegacyForge) { + configureRuns(json.getAsJsonObject("runs")); + } else { + configureRunsForLegacyForge(); + } + } + private void configureRuns(JsonObject runs) { for (Map.Entry entry : runs.entrySet()) { LaunchProviderSettings launchSettings = getExtension().getLaunchConfigs().findByName(entry.getKey()); RunConfigSettings settings = getExtension().getRunConfigs().findByName(entry.getKey()); @@ -211,14 +217,14 @@ public void provide(DependencyInfo dependency, Consumer postPopulation }); } } + } - if (isLegacyForge) { - getExtension().getRunConfigs().configureEach(config -> { - if (Constants.Forge.LAUNCH_TESTING.equals(config.getDefaultMainClass())) { - config.setDefaultMainClass(Constants.LegacyForge.LAUNCH_WRAPPER); - } - }); - } + private void configureRunsForLegacyForge() { + getExtension().getRunConfigs().configureEach(config -> { + if (Constants.Forge.LAUNCH_TESTING.equals(config.getDefaultMainClass())) { + config.setDefaultMainClass(Constants.LegacyForge.LAUNCH_WRAPPER); + } + }); } public boolean isLegacyForge() { From beb659eb6cde56b560531bc61abc3db3b02d8702 Mon Sep 17 00:00:00 2001 From: Jonas Herzig Date: Tue, 11 Jan 2022 16:32:38 +0100 Subject: [PATCH 09/44] Rename `isModLauncher` -> `isModernForge` Usages are more related to the modern Forge than to ModLauncher specifically. --- src/main/java/net/fabricmc/loom/LoomGradleExtension.java | 2 +- .../fabricmc/loom/configuration/providers/LaunchProvider.java | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/main/java/net/fabricmc/loom/LoomGradleExtension.java b/src/main/java/net/fabricmc/loom/LoomGradleExtension.java index b5566765f..06208777d 100644 --- a/src/main/java/net/fabricmc/loom/LoomGradleExtension.java +++ b/src/main/java/net/fabricmc/loom/LoomGradleExtension.java @@ -143,7 +143,7 @@ default boolean isLegacyForge() { return isForge() && getForgeUserdevProvider().isLegacyForge(); } - default boolean isModLauncher() { + default boolean isModernForge() { return isForge() && !isLegacyForge(); } diff --git a/src/main/java/net/fabricmc/loom/configuration/providers/LaunchProvider.java b/src/main/java/net/fabricmc/loom/configuration/providers/LaunchProvider.java index ae52c76ff..a8970bca6 100644 --- a/src/main/java/net/fabricmc/loom/configuration/providers/LaunchProvider.java +++ b/src/main/java/net/fabricmc/loom/configuration/providers/LaunchProvider.java @@ -66,7 +66,7 @@ public void provide(DependencyInfo dependency, Consumer postPopulation .property("client", "java.library.path", getExtension().getMinecraftProvider().nativesDir().getAbsolutePath()) .property("client", "org.lwjgl.librarypath", getExtension().getMinecraftProvider().nativesDir().getAbsolutePath()); - if (!getExtension().isModLauncher()) { + if (!getExtension().isModernForge()) { launchConfig .argument("client", "--assetIndex") .argument("client", getExtension().getMinecraftProvider().getVersionInfo().assetIndex().fabricId(getExtension().getMinecraftProvider().minecraftVersion())) @@ -74,7 +74,7 @@ public void provide(DependencyInfo dependency, Consumer postPopulation .argument("client", new File(getDirectories().getUserCache(), "assets").getAbsolutePath()); } - if (getExtension().isModLauncher()) { + if (getExtension().isModernForge()) { launchConfig // Should match YarnNamingService.PATH_TO_MAPPINGS in forge-runtime .property("fabric.yarnWithSrg.path", getExtension().getMappingsProvider().tinyMappingsWithSrg.toAbsolutePath().toString()) From 9eaae61380c419be4cdd79f135d1a39c672cb950 Mon Sep 17 00:00:00 2001 From: Jonas Herzig Date: Tue, 11 Jan 2022 16:45:32 +0100 Subject: [PATCH 10/44] Add comments to non-obvious legacy code paths --- .../loom/configuration/providers/forge/ForgeUserdevProvider.java | 1 + .../fabricmc/loom/configuration/providers/forge/SrgProvider.java | 1 + 2 files changed, 2 insertions(+) diff --git a/src/main/java/net/fabricmc/loom/configuration/providers/forge/ForgeUserdevProvider.java b/src/main/java/net/fabricmc/loom/configuration/providers/forge/ForgeUserdevProvider.java index b30e78f20..37e4902d2 100644 --- a/src/main/java/net/fabricmc/loom/configuration/providers/forge/ForgeUserdevProvider.java +++ b/src/main/java/net/fabricmc/loom/configuration/providers/forge/ForgeUserdevProvider.java @@ -102,6 +102,7 @@ public void provide(DependencyInfo dependency, Consumer postPopulation try (FileSystem fs = FileSystems.newFileSystem(new URI("jar:" + resolved.toURI()), ImmutableMap.of("create", false))) { Path configEntry = fs.getPath("config.json"); + // If we cannot find a modern config json, try the legacy/FG2-era one if (!Files.exists(configEntry)) { configEntry = fs.getPath("dev.json"); } diff --git a/src/main/java/net/fabricmc/loom/configuration/providers/forge/SrgProvider.java b/src/main/java/net/fabricmc/loom/configuration/providers/forge/SrgProvider.java index 920d269ef..9c24416c9 100644 --- a/src/main/java/net/fabricmc/loom/configuration/providers/forge/SrgProvider.java +++ b/src/main/java/net/fabricmc/loom/configuration/providers/forge/SrgProvider.java @@ -94,6 +94,7 @@ public void provide(DependencyInfo dependency, Consumer postPopulation Path srgPath = fs.getPath("joined.srg"); if (Files.exists(srgPath)) { + // FG2-era MCP uses the older SRG format, convert it on the fly try (Reader reader = Files.newBufferedReader(srgPath); Writer writer = Files.newBufferedWriter(srg)) { new TSrgWriter(writer).write(new SrgReader(reader).read()); } From 75f0fa8c8969091a4b4e1fe07d941a0215b8fd83 Mon Sep 17 00:00:00 2001 From: Jonas Herzig Date: Tue, 11 Jan 2022 17:15:36 +0100 Subject: [PATCH 11/44] Flip a few `if`s so the modern code path comes before the legacy one --- .../providers/forge/ForgeUserdevProvider.java | 12 ++++++------ .../configuration/providers/forge/PatchProvider.java | 12 ++++++------ .../providers/mappings/MappingsProviderImpl.java | 12 ++++++------ 3 files changed, 18 insertions(+), 18 deletions(-) diff --git a/src/main/java/net/fabricmc/loom/configuration/providers/forge/ForgeUserdevProvider.java b/src/main/java/net/fabricmc/loom/configuration/providers/forge/ForgeUserdevProvider.java index 37e4902d2..97a69127a 100644 --- a/src/main/java/net/fabricmc/loom/configuration/providers/forge/ForgeUserdevProvider.java +++ b/src/main/java/net/fabricmc/loom/configuration/providers/forge/ForgeUserdevProvider.java @@ -103,7 +103,7 @@ public void provide(DependencyInfo dependency, Consumer postPopulation Path configEntry = fs.getPath("config.json"); // If we cannot find a modern config json, try the legacy/FG2-era one - if (!Files.exists(configEntry)) { + if (Files.notExists(configEntry)) { configEntry = fs.getPath("dev.json"); } @@ -117,7 +117,11 @@ public void provide(DependencyInfo dependency, Consumer postPopulation isLegacyForge = !json.has("mcp"); - if (isLegacyForge) { + if (!isLegacyForge) { + addDependency(json.get("mcp").getAsString(), Constants.Configurations.MCP_CONFIG); + addDependency(json.get("mcp").getAsString(), Constants.Configurations.SRG); + addDependency(json.get("universal").getAsString(), Constants.Configurations.FORGE_UNIVERSAL); + } else { Map mcpDep = Map.of( "group", "de.oceanlabs.mcp", "name", "mcp", @@ -128,10 +132,6 @@ public void provide(DependencyInfo dependency, Consumer postPopulation addDependency(mcpDep, Constants.Configurations.MCP_CONFIG); addDependency(mcpDep, Constants.Configurations.SRG); addDependency(dependency.getDepString() + ":universal", Constants.Configurations.FORGE_UNIVERSAL); - } else { - addDependency(json.get("mcp").getAsString(), Constants.Configurations.MCP_CONFIG); - addDependency(json.get("mcp").getAsString(), Constants.Configurations.SRG); - addDependency(json.get("universal").getAsString(), Constants.Configurations.FORGE_UNIVERSAL); } for (JsonElement lib : json.get("libraries").getAsJsonArray()) { diff --git a/src/main/java/net/fabricmc/loom/configuration/providers/forge/PatchProvider.java b/src/main/java/net/fabricmc/loom/configuration/providers/forge/PatchProvider.java index ca55e8d7d..bfd36d938 100644 --- a/src/main/java/net/fabricmc/loom/configuration/providers/forge/PatchProvider.java +++ b/src/main/java/net/fabricmc/loom/configuration/providers/forge/PatchProvider.java @@ -75,16 +75,16 @@ public void provide(DependencyInfo dependency, Consumer postPopulation if (Files.notExists(clientPatches) || Files.notExists(serverPatches) || isRefreshDeps()) { getProject().getLogger().info(":extracting forge patches"); - Path installerJar = getExtension().isLegacyForge() - ? getExtension().getForgeUniversalProvider().getForge().toPath() - : dependency.resolveFile().orElseThrow(() -> new RuntimeException("Could not resolve Forge installer")).toPath(); + Path installerJar = getExtension().isModernForge() + ? dependency.resolveFile().orElseThrow(() -> new RuntimeException("Could not resolve Forge installer")).toPath() + : getExtension().getForgeUniversalProvider().getForge().toPath(); try (FileSystem fs = FileSystems.newFileSystem(new URI("jar:" + installerJar.toUri()), ImmutableMap.of("create", false))) { - if (getExtension().isLegacyForge()) { - splitAndConvertLegacyPatches(fs.getPath("binpatches.pack.lzma")); - } else { + if (getExtension().isModernForge()) { Files.copy(fs.getPath("data", "client.lzma"), clientPatches, StandardCopyOption.REPLACE_EXISTING); Files.copy(fs.getPath("data", "server.lzma"), serverPatches, StandardCopyOption.REPLACE_EXISTING); + } else { + splitAndConvertLegacyPatches(fs.getPath("binpatches.pack.lzma")); } } } diff --git a/src/main/java/net/fabricmc/loom/configuration/providers/mappings/MappingsProviderImpl.java b/src/main/java/net/fabricmc/loom/configuration/providers/mappings/MappingsProviderImpl.java index 51cc4ef41..3be857bd6 100644 --- a/src/main/java/net/fabricmc/loom/configuration/providers/mappings/MappingsProviderImpl.java +++ b/src/main/java/net/fabricmc/loom/configuration/providers/mappings/MappingsProviderImpl.java @@ -152,10 +152,10 @@ public void provide(DependencyInfo dependency, Consumer postPopulation } if (getExtension().isForge()) { - if (getExtension().isLegacyForge()) { - patchedProvider = new MinecraftLegacyPatchedProvider(getProject()); - } else { + if (getExtension().isModernForge()) { patchedProvider = new MinecraftPatchedProvider(getProject()); + } else { + patchedProvider = new MinecraftLegacyPatchedProvider(getProject()); } patchedProvider.provide(dependency, postPopulationScheduler); @@ -588,14 +588,14 @@ public Path getIntermediaryTiny() throws IOException { hasRefreshed = true; // Download and extract intermediary - if (getExtension().isLegacyForge()) { - generateDummyIntermediary(getProject().getLogger(), intermediaryTiny); - } else { + if (!getExtension().isLegacyForge()) { String encodedMinecraftVersion = UrlEscapers.urlFragmentEscaper().escape(getMinecraftProvider().minecraftVersion()); String intermediaryArtifactUrl = getExtension().getIntermediaryUrl(encodedMinecraftVersion); File intermediaryJar = getMinecraftProvider().file("intermediary-v2.jar"); DownloadUtil.downloadIfChanged(new URL(intermediaryArtifactUrl), intermediaryJar, getProject().getLogger()); extractMappings(intermediaryJar.toPath(), intermediaryTiny); + } else { + generateDummyIntermediary(getProject().getLogger(), intermediaryTiny); } } } From 8e30bfd92ca47f6b32b96d5f89c5817c4a75adbe Mon Sep 17 00:00:00 2001 From: Jonas Herzig Date: Tue, 11 Jan 2022 17:23:27 +0100 Subject: [PATCH 12/44] Only add LegacyMCP repo when using a legacy version --- .../fabricmc/loom/LoomRepositoryPlugin.java | 14 -------------- .../providers/forge/ForgeUserdevProvider.java | 19 +++++++++++++++++++ 2 files changed, 19 insertions(+), 14 deletions(-) diff --git a/src/main/java/net/fabricmc/loom/LoomRepositoryPlugin.java b/src/main/java/net/fabricmc/loom/LoomRepositoryPlugin.java index 3d93bad6b..280450717 100644 --- a/src/main/java/net/fabricmc/loom/LoomRepositoryPlugin.java +++ b/src/main/java/net/fabricmc/loom/LoomRepositoryPlugin.java @@ -90,20 +90,6 @@ private void declareRepositories(RepositoryHandler repositories, LoomFiles files sources.ignoreGradleMetadataRedirection(); }); }); - repositories.ivy(repo -> { - // Old MCP data does not have POMs - repo.setName("LegacyMCP"); - repo.setUrl("https://maven.minecraftforge.net/"); - repo.patternLayout(layout -> { - layout.artifact("[orgPath]/[artifact]/[revision]/[artifact]-[revision](-[classifier])(.[ext])"); - // also check the zip so people do not have to explicitly specify the extension for older versions - layout.artifact("[orgPath]/[artifact]/[revision]/[artifact]-[revision](-[classifier]).zip"); - }); - repo.content(descriptor -> { - descriptor.includeGroup("de.oceanlabs.mcp"); - }); - repo.metadataSources(IvyArtifactRepository.MetadataSources::artifact); - }); repositories.mavenCentral(); repositories.ivy(repo -> { diff --git a/src/main/java/net/fabricmc/loom/configuration/providers/forge/ForgeUserdevProvider.java b/src/main/java/net/fabricmc/loom/configuration/providers/forge/ForgeUserdevProvider.java index 97a69127a..b74361243 100644 --- a/src/main/java/net/fabricmc/loom/configuration/providers/forge/ForgeUserdevProvider.java +++ b/src/main/java/net/fabricmc/loom/configuration/providers/forge/ForgeUserdevProvider.java @@ -50,6 +50,7 @@ import org.gradle.api.Project; import org.gradle.api.artifacts.Dependency; import org.gradle.api.artifacts.ModuleDependency; +import org.gradle.api.artifacts.repositories.IvyArtifactRepository; import org.gradle.api.artifacts.transform.InputArtifact; import org.gradle.api.artifacts.transform.TransformAction; import org.gradle.api.artifacts.transform.TransformOutputs; @@ -132,6 +133,7 @@ public void provide(DependencyInfo dependency, Consumer postPopulation addDependency(mcpDep, Constants.Configurations.MCP_CONFIG); addDependency(mcpDep, Constants.Configurations.SRG); addDependency(dependency.getDepString() + ":universal", Constants.Configurations.FORGE_UNIVERSAL); + addLegacyMCPRepo(); } for (JsonElement lib : json.get("libraries").getAsJsonArray()) { @@ -228,6 +230,23 @@ private void configureRunsForLegacyForge() { }); } + private void addLegacyMCPRepo() { + getProject().getRepositories().ivy(repo -> { + // Old MCP data does not have POMs + repo.setName("LegacyMCP"); + repo.setUrl("https://maven.minecraftforge.net/"); + repo.patternLayout(layout -> { + layout.artifact("[orgPath]/[artifact]/[revision]/[artifact]-[revision](-[classifier])(.[ext])"); + // also check the zip so people do not have to explicitly specify the extension for older versions + layout.artifact("[orgPath]/[artifact]/[revision]/[artifact]-[revision](-[classifier]).zip"); + }); + repo.content(descriptor -> { + descriptor.includeGroup("de.oceanlabs.mcp"); + }); + repo.metadataSources(IvyArtifactRepository.MetadataSources::artifact); + }); + } + public boolean isLegacyForge() { return isLegacyForge; } From 7f33c126b65ad101ccb2a758b7a5d23ce84f5f64 Mon Sep 17 00:00:00 2001 From: Jonas Herzig Date: Tue, 11 Jan 2022 17:55:06 +0100 Subject: [PATCH 13/44] Do not add mixin to AP configs during IDE sync even for legacy forge I do not quite remember why I added this in the first place but it seems fine without, so let's not mess with it. --- .../fabricmc/loom/build/mixin/AnnotationProcessorInvoker.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/net/fabricmc/loom/build/mixin/AnnotationProcessorInvoker.java b/src/main/java/net/fabricmc/loom/build/mixin/AnnotationProcessorInvoker.java index 1793ca725..33fec4e6e 100644 --- a/src/main/java/net/fabricmc/loom/build/mixin/AnnotationProcessorInvoker.java +++ b/src/main/java/net/fabricmc/loom/build/mixin/AnnotationProcessorInvoker.java @@ -101,7 +101,7 @@ public void configureMixin() { ConfigurationContainer configs = project.getConfigurations(); LoomGradleExtension extension = LoomGradleExtension.get(project); - if (!extension.ideSync() || extension.isLegacyForge()) { + if (!extension.ideSync()) { for (Configuration processorConfig : apConfigurations) { project.getLogger().info("Adding mixin to classpath of AP config: " + processorConfig.getName()); // Pass named MC classpath to mixin AP classpath From 4fa31a4c987e48d58a05c6b0ecd239825dd2b2cd Mon Sep 17 00:00:00 2001 From: Jonas Herzig Date: Tue, 11 Jan 2022 20:34:01 +0100 Subject: [PATCH 14/44] Re-use forge accesstransformer tool for legacy forge --- build.gradle | 3 - .../forge/MinecraftPatchedProvider.java | 45 +++++--- .../fg2/MinecraftLegacyPatchedProvider.java | 108 +++++------------- .../util/srg/AccessTransformSetMapper.java | 70 ++++++++++++ 4 files changed, 123 insertions(+), 103 deletions(-) create mode 100644 src/main/java/net/fabricmc/loom/util/srg/AccessTransformSetMapper.java diff --git a/build.gradle b/build.gradle index ef0a1da80..d88a512ce 100644 --- a/build.gradle +++ b/build.gradle @@ -119,9 +119,6 @@ dependencies { implementation ('de.oceanlabs.mcp:mcinjector:3.8.0') implementation ('com.opencsv:opencsv:5.4') - // Legacy Forge access transformers - implementation ('net.md-5:SpecialSource:1.10.0') - // Testing testImplementation(gradleTestKit()) testImplementation('org.spockframework:spock-core:2.0-groovy-3.0') { diff --git a/src/main/java/net/fabricmc/loom/configuration/providers/forge/MinecraftPatchedProvider.java b/src/main/java/net/fabricmc/loom/configuration/providers/forge/MinecraftPatchedProvider.java index 05a216bd6..f11907dc2 100644 --- a/src/main/java/net/fabricmc/loom/configuration/providers/forge/MinecraftPatchedProvider.java +++ b/src/main/java/net/fabricmc/loom/configuration/providers/forge/MinecraftPatchedProvider.java @@ -477,6 +477,26 @@ protected boolean isPatchedJarUpToDate(File jar) throws IOException { } private void accessTransformForge(Logger logger) throws Exception { + List ats = new ArrayList<>(); + + for (File jar : ImmutableList.of(getForgeJar(), getForgeUserdevJar(), minecraftMergedPatchedSrgJar)) { + byte[] atBytes = ZipUtils.unpackNullable(jar.toPath(), Constants.Forge.ACCESS_TRANSFORMER_PATH); + + if (atBytes != null) { + ats.add(atBytes); + } + } + + if (usesProjectCache()) { + for (File projectAt : projectAts) { + ats.add(Files.readAllBytes(projectAt.toPath())); + } + } + + accessTransformForge(logger, minecraftMergedPatchedSrgJar, minecraftMergedPatchedSrgAtJar, ats); + } + + protected void accessTransformForge(Logger logger, File input, File target, List ats) throws Exception { MinecraftProviderImpl minecraftProvider = getExtension().getMinecraftProvider(); List toDelete = new ArrayList<>(); String atDependency = Constants.Dependencies.ACCESS_TRANSFORMERS + (minecraftProvider.isNewerThan21w39a() ? Constants.Dependencies.Versions.ACCESS_TRANSFORMERS_NEW : Constants.Dependencies.Versions.ACCESS_TRANSFORMERS); @@ -485,8 +505,6 @@ private void accessTransformForge(Logger logger) throws Exception { logger.lifecycle(":access transforming minecraft"); - File input = minecraftMergedPatchedSrgJar; - File target = minecraftMergedPatchedSrgAtJar; Files.deleteIfExists(target.toPath()); List args = new ArrayList<>(); @@ -495,23 +513,12 @@ private void accessTransformForge(Logger logger) throws Exception { args.add("--outJar"); args.add(target.getAbsolutePath()); - for (File jar : ImmutableList.of(getForgeJar(), getForgeUserdevJar(), minecraftMergedPatchedSrgJar)) { - byte[] atBytes = ZipUtils.unpackNullable(jar.toPath(), Constants.Forge.ACCESS_TRANSFORMER_PATH); - - if (atBytes != null) { - File tmpFile = File.createTempFile("at-conf", ".cfg"); - toDelete.add(tmpFile); - Files.write(tmpFile.toPath(), atBytes, StandardOpenOption.CREATE, StandardOpenOption.TRUNCATE_EXISTING); - args.add("--atFile"); - args.add(tmpFile.getAbsolutePath()); - } - } - - if (usesProjectCache()) { - for (File projectAt : projectAts) { - args.add("--atFile"); - args.add(projectAt.getAbsolutePath()); - } + for (byte[] atBytes : ats) { + File tmpFile = File.createTempFile("at-conf", ".cfg"); + toDelete.add(tmpFile); + Files.write(tmpFile.toPath(), atBytes, StandardOpenOption.CREATE, StandardOpenOption.TRUNCATE_EXISTING); + args.add("--atFile"); + args.add(tmpFile.getAbsolutePath()); } getProject().javaexec(spec -> { diff --git a/src/main/java/net/fabricmc/loom/configuration/providers/forge/fg2/MinecraftLegacyPatchedProvider.java b/src/main/java/net/fabricmc/loom/configuration/providers/forge/fg2/MinecraftLegacyPatchedProvider.java index e87004c23..e74e463e3 100644 --- a/src/main/java/net/fabricmc/loom/configuration/providers/forge/fg2/MinecraftLegacyPatchedProvider.java +++ b/src/main/java/net/fabricmc/loom/configuration/providers/forge/fg2/MinecraftLegacyPatchedProvider.java @@ -24,11 +24,13 @@ package net.fabricmc.loom.configuration.providers.forge.fg2; -import java.io.BufferedReader; +import java.io.BufferedWriter; import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; import java.io.File; import java.io.IOException; import java.io.InputStreamReader; +import java.io.OutputStreamWriter; import java.net.URI; import java.nio.file.FileSystem; import java.nio.file.FileSystems; @@ -37,6 +39,7 @@ import java.nio.file.StandardCopyOption; import java.nio.file.StandardOpenOption; import java.util.Arrays; +import java.util.Collections; import java.util.function.Consumer; import java.util.function.Function; import java.util.function.Predicate; @@ -44,8 +47,9 @@ import com.google.common.base.Stopwatch; import com.google.common.collect.ImmutableMap; -import net.md_5.specialsource.AccessChange; -import net.md_5.specialsource.AccessMap; +import org.cadixdev.at.AccessTransformSet; +import org.cadixdev.at.io.AccessTransformFormats; +import org.cadixdev.lorenz.MappingSet; import org.gradle.api.Project; import org.gradle.api.logging.Logger; import org.objectweb.asm.AnnotationVisitor; @@ -63,6 +67,8 @@ import net.fabricmc.loom.util.ThreadingUtils; import net.fabricmc.loom.util.TinyRemapperHelper; import net.fabricmc.loom.util.ZipUtils; +import net.fabricmc.loom.util.srg.AccessTransformSetMapper; +import net.fabricmc.lorenztiny.TinyMappingsReader; import net.fabricmc.mappingio.tree.MappingTree; import net.fabricmc.stitch.merge.JarMerger; @@ -224,26 +230,30 @@ private void mergeJars(Logger logger) throws Exception { } private void accessTransformForge(Logger logger) throws Exception { - Stopwatch stopwatch = Stopwatch.createStarted(); - - logger.lifecycle(":access transforming minecraft"); - - MappingTree mappingTree = getExtension().getMappingsProvider().getMappingsWithSrg(); - - AccessMap accessMap = new LegacyAccessMap(); + // Load all applicable access transformers + AccessTransformSet accessTransformSet = AccessTransformSet.create(); byte[] forgeAt = ZipUtils.unpack(forgeJar.toPath(), "forge_at.cfg"); - accessMap.loadAccessTransformer(new BufferedReader(new InputStreamReader(new ByteArrayInputStream(forgeAt)))); + AccessTransformFormats.FML.read(new InputStreamReader(new ByteArrayInputStream(forgeAt)), accessTransformSet); for (File projectAt : projectAts) { - accessMap.loadAccessTransformer(projectAt); + AccessTransformFormats.FML.read(projectAt.toPath(), accessTransformSet); } - Files.copy(minecraftMergedPatchedJar.toPath(), minecraftMergedPatchedAtJar.toPath(), StandardCopyOption.REPLACE_EXISTING); - - modifyClasses(minecraftMergedPatchedAtJar, writer -> new AccessTransformingVisitor(accessMap, mappingTree, writer)); - - logger.lifecycle(":access transformed minecraft in " + stopwatch.stop()); + // Remap them from srg to official mappings + MappingTree mappingTree = getExtension().getMappingsProvider().getMappingsWithSrg(); + MappingSet mappingSet = new TinyMappingsReader(mappingTree, "srg", "official").read(); + accessTransformSet = AccessTransformSetMapper.remap(accessTransformSet, mappingSet); + + ByteArrayOutputStream remappedOut = new ByteArrayOutputStream(); + // TODO the extra BufferedWriter wrapper and closing can be removed once https://github.com/CadixDev/at/issues/6 is fixed + BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(remappedOut)); + AccessTransformFormats.FML.write(writer, accessTransformSet); + writer.close(); + byte[] remappedAt = remappedOut.toByteArray(); + + // And finally, apply them to the merged+patched jar + accessTransformForge(logger, minecraftMergedPatchedJar, minecraftMergedPatchedAtJar, Collections.singletonList(remappedAt)); } private void modifyClasses(File jarFile, Function func) throws Exception { @@ -380,68 +390,4 @@ public AnnotationVisitor visitAnnotation(String descriptor, boolean visible) { } } } - - private static class LegacyAccessMap extends AccessMap { - @Override - public void addAccessChange(String key, AccessChange accessChange) { - // Forge's AT separates fields/methods from their owner by a space but we require a slash - int spaceIdx = key.indexOf(' '); - - if (spaceIdx != -1 && key.charAt(spaceIdx + 1) != '(') { - key = key.replaceFirst(" ", "/"); - } - - super.addAccessChange(key, accessChange); - } - } - - private static class AccessTransformingVisitor extends ClassVisitor { - private final AccessMap accessMap; - private final MappingTree mappingTree; - private final int src; - private final int dst; - - private MappingTree.ClassMapping classMapping; - private String mappedClassName; - - private AccessTransformingVisitor(AccessMap accessMap, MappingTree mappingTree, ClassVisitor classVisitor) { - super(Opcodes.ASM9, classVisitor); - this.accessMap = accessMap; - this.mappingTree = mappingTree; - this.src = mappingTree.getNamespaceId("official"); - this.dst = mappingTree.getNamespaceId("srg"); - } - - @Override - public void visit(int version, int access, String name, String signature, String superName, String[] interfaces) { - classMapping = mappingTree.getClass(name, src); - mappedClassName = classMapping != null ? classMapping.getName(dst) : name; - access = accessMap.applyClassAccess(mappedClassName, access); - super.visit(version, access, name, signature, superName, interfaces); - } - - @Override - public FieldVisitor visitField(int access, String name, String descriptor, String signature, Object value) { - MappingTree.FieldMapping field = classMapping != null ? classMapping.getField(name, descriptor, src) : null; - String mappedName = field != null ? field.getName(dst) : name; - access = accessMap.applyFieldAccess(mappedClassName, mappedName, access); - return super.visitField(access, name, descriptor, signature, value); - } - - @Override - public MethodVisitor visitMethod(int access, String name, String desc, String signature, String[] exceptions) { - MappingTree.MethodMapping method = classMapping != null ? classMapping.getMethod(name, desc, src) : null; - String mappedName = method != null ? method.getName(dst) : name; - String mappedDesc = method != null ? method.getDesc(dst) : mappingTree.mapDesc(desc, src, dst); - access = accessMap.applyMethodAccess(mappedClassName, mappedName, mappedDesc, access); - return super.visitMethod(access, name, desc, signature, exceptions); - } - - @Override - public void visitInnerClass(String name, String outerName, String innerName, int access) { - MappingTree.ClassMapping classMapping = mappingTree.getClass(name, src); - access = accessMap.applyClassAccess(classMapping != null ? classMapping.getName(dst) : name, access); - super.visitInnerClass(name, outerName, innerName, access); - } - } } diff --git a/src/main/java/net/fabricmc/loom/util/srg/AccessTransformSetMapper.java b/src/main/java/net/fabricmc/loom/util/srg/AccessTransformSetMapper.java new file mode 100644 index 000000000..d17eabfb1 --- /dev/null +++ b/src/main/java/net/fabricmc/loom/util/srg/AccessTransformSetMapper.java @@ -0,0 +1,70 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2018 Minecrell (https://github.com/Minecrell) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +package net.fabricmc.loom.util.srg; + +import java.util.Objects; +import java.util.Optional; + +import org.cadixdev.at.AccessTransformSet; +import org.cadixdev.bombe.type.signature.MethodSignature; +import org.cadixdev.lorenz.MappingSet; +import org.cadixdev.lorenz.model.ClassMapping; +import org.cadixdev.lorenz.model.FieldMapping; +import org.cadixdev.lorenz.model.Mapping; +import org.cadixdev.lorenz.model.MethodMapping; + +// TODO from https://github.com/CadixDev/at/blob/c2b92fc26cf26e64d3ca3b35abf3364d4b95e6c3/src/main/java/org/cadixdev/at/impl/AccessTransformSetMapper.java +// remove once https://github.com/CadixDev/at/issues/7 is fixed +public final class AccessTransformSetMapper { + private AccessTransformSetMapper() { + } + + public static AccessTransformSet remap(AccessTransformSet set, MappingSet mappings) { + Objects.requireNonNull(set, "set"); + Objects.requireNonNull(mappings, "mappings"); + + AccessTransformSet remapped = AccessTransformSet.create(); + set.getClasses().forEach((className, classSet) -> { + Optional> mapping = mappings.getClassMapping(className); + remap(mappings, mapping, classSet, remapped.getOrCreateClass(mapping.map(Mapping::getFullDeobfuscatedName).orElse(className))); + }); + return remapped; + } + + private static void remap(MappingSet mappings, Optional> mapping, AccessTransformSet.Class set, AccessTransformSet.Class remapped) { + remapped.merge(set.get()); + remapped.mergeAllFields(set.allFields()); + remapped.mergeAllMethods(set.allMethods()); + + set.getFields().forEach((name, transform) -> + remapped.mergeField(mapping.flatMap(m -> m.getFieldMapping(name)) + .map(FieldMapping::getDeobfuscatedName).orElse(name), transform)); + + set.getMethods().forEach((signature, transform) -> + remapped.mergeMethod(mapping.flatMap(m -> m.getMethodMapping(signature)) + .map(MethodMapping::getDeobfuscatedSignature) + .orElseGet(() -> new MethodSignature(signature.getName(), mappings.deobfuscate(signature.getDescriptor()))), transform)); + } +} From 5f33bfd5edfdeb29e9f4dd90a6c768825b91e56a Mon Sep 17 00:00:00 2001 From: Jonas Herzig Date: Tue, 11 Jan 2022 20:38:26 +0100 Subject: [PATCH 15/44] Simplify `isForge && !isLegacyForge` -> `isModernForge` --- .../fabricmc/loom/configuration/providers/LaunchProvider.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/net/fabricmc/loom/configuration/providers/LaunchProvider.java b/src/main/java/net/fabricmc/loom/configuration/providers/LaunchProvider.java index a8970bca6..27fe1ba53 100644 --- a/src/main/java/net/fabricmc/loom/configuration/providers/LaunchProvider.java +++ b/src/main/java/net/fabricmc/loom/configuration/providers/LaunchProvider.java @@ -125,7 +125,7 @@ public void provide(DependencyInfo dependency, Consumer postPopulation addDependency(Constants.Dependencies.TERMINAL_CONSOLE_APPENDER + Constants.Dependencies.Versions.TERMINAL_CONSOLE_APPENDER, Constants.Configurations.LOOM_DEVELOPMENT_DEPENDENCIES); addDependency(Constants.Dependencies.JETBRAINS_ANNOTATIONS + Constants.Dependencies.Versions.JETBRAINS_ANNOTATIONS, JavaPlugin.COMPILE_ONLY_CONFIGURATION_NAME); - if (getExtension().isForge() && !getExtension().isLegacyForge()) { + if (getExtension().isModernForge()) { addDependency(Constants.Dependencies.FORGE_RUNTIME + Constants.Dependencies.Versions.FORGE_RUNTIME, Constants.Configurations.FORGE_EXTRA); addDependency(Constants.Dependencies.JAVAX_ANNOTATIONS + Constants.Dependencies.Versions.JAVAX_ANNOTATIONS, JavaPlugin.COMPILE_ONLY_CONFIGURATION_NAME); } From 234f9ccbfd2e67ba9cbbcc67b28dcfb5f6c4ffef Mon Sep 17 00:00:00 2001 From: Jonas Herzig Date: Thu, 17 Feb 2022 10:54:34 +0100 Subject: [PATCH 16/44] Reconfigure plugin id and repo for essential maven --- bootstrap/test-project/build.gradle | 2 +- build.gradle | 21 ++++++++++--------- .../net/fabricmc/loom/util/Constants.java | 2 +- .../test/integration/FabricAPITest.groovy | 4 ++-- .../projects/accesswidener/build.gradle | 2 +- .../resources/projects/decompile/build.gradle | 2 +- .../basic/build.gradle | 2 +- .../projmap/build.gradle | 2 +- .../settings.gradle | 4 ++-- .../projects/forge/aw2At/build.gradle | 2 +- .../forge/legacyAw2AtMojmap/build.gradle | 2 +- .../forge/legacyAw2AtYarn/build.gradle | 2 +- .../projects/forge/simple/build.gradle | 2 +- .../resources/projects/java16/build.gradle | 2 +- .../projects/kotlin/build.gradle.kts | 2 +- .../resources/projects/legacy/build.gradle | 2 +- .../projects/localFileDependency/build.gradle | 2 +- .../resources/projects/maven/build.gradle | 2 +- .../projects/mavenLibrary/build.gradle | 2 +- .../projects/minimalBase/build.gradle | 2 +- .../projects/mixinApAutoRefmap/build.gradle | 2 +- .../projects/mixinApSimple/build.gradle | 2 +- .../projects/mojangMappings/build.gradle | 2 +- .../projects/multiproject/build.gradle | 4 ++-- .../resources/projects/parchment/build.gradle | 2 +- .../projects/reproducible/build.gradle | 2 +- .../projects/runconfigs/build.gradle | 2 +- .../resources/projects/signed/build.gradle | 2 +- .../resources/projects/simple/build.gradle | 2 +- .../transitiveAccesswidener/build.gradle | 2 +- .../resources/projects/unpick/build.gradle | 2 +- 31 files changed, 44 insertions(+), 43 deletions(-) diff --git a/bootstrap/test-project/build.gradle b/bootstrap/test-project/build.gradle index ab88e467a..578e50ab4 100644 --- a/bootstrap/test-project/build.gradle +++ b/bootstrap/test-project/build.gradle @@ -1,5 +1,5 @@ plugins { - id 'dev.architectury.loom' version '0.10.local' + id 'gg.essential.loom' version '0.10.local' } dependencies { diff --git a/build.gradle b/build.gradle index 2cc27e478..df0341872 100644 --- a/build.gradle +++ b/build.gradle @@ -21,7 +21,7 @@ tasks.withType(JavaCompile).configureEach { it.options.release = 16 } -group = "dev.architectury" +group = "gg.essential" archivesBaseName = project.name def baseVersion = '0.10.0' def runNumber = System.getenv("GITHUB_RUN_NUMBER") ?: "9999" @@ -191,7 +191,7 @@ codenarc { gradlePlugin { plugins { fabricLoom { - id = 'dev.architectury.loom' + id = 'gg.essential.loom' implementationClass = 'net.fabricmc.loom.bootstrap.LoomGradlePluginBootstrap' } } @@ -227,8 +227,8 @@ import org.w3c.dom.Node publishing { publications { plugin(MavenPublication) { - groupId 'dev.architectury.loom' - artifactId 'dev.architectury.loom.gradle.plugin' + groupId 'gg.essential.loom' + artifactId 'gg.essential.loom.gradle.plugin' from components.java artifact downgradeJava @@ -259,8 +259,8 @@ publishing { } pluginSnapshot(MavenPublication) { - groupId 'dev.architectury.loom' - artifactId 'dev.architectury.loom.gradle.plugin' + groupId 'gg.essential.loom' + artifactId 'gg.essential.loom.gradle.plugin' version baseVersion + '-SNAPSHOT' pom.withXml { @@ -280,12 +280,13 @@ publishing { } repositories { - if (System.getenv("MAVEN_PASS") != null) { + if (project.hasProperty("nexus_user")) { maven { - url = "https://deploy.shedaniel.me/" + url = "https://repo.sk1er.club/repository/maven-releases/" + name = "nexus-public" credentials { - username = "shedaniel" - password = System.getenv("MAVEN_PASS") + username = project.property("nexus_user").toString() + password = project.property("nexus_password").toString() } } } diff --git a/src/main/java/net/fabricmc/loom/util/Constants.java b/src/main/java/net/fabricmc/loom/util/Constants.java index 8a68b0fb5..0a2d30288 100644 --- a/src/main/java/net/fabricmc/loom/util/Constants.java +++ b/src/main/java/net/fabricmc/loom/util/Constants.java @@ -34,7 +34,7 @@ import net.fabricmc.loom.configuration.RemappedConfigurationEntry.PublishingMode; public class Constants { - public static final String PLUGIN_ID = "dev.architectury.loom"; + public static final String PLUGIN_ID = "gg.essential.loom"; public static final String LIBRARIES_BASE = "https://libraries.minecraft.net/"; public static final String RESOURCES_BASE = "https://resources.download.minecraft.net/"; public static final String VERSION_MANIFESTS = "https://launchermeta.mojang.com/mc/game/version_manifest_v2.json"; diff --git a/src/test/groovy/net/fabricmc/loom/test/integration/FabricAPITest.groovy b/src/test/groovy/net/fabricmc/loom/test/integration/FabricAPITest.groovy index 3ce597105..d5adb19d6 100644 --- a/src/test/groovy/net/fabricmc/loom/test/integration/FabricAPITest.groovy +++ b/src/test/groovy/net/fabricmc/loom/test/integration/FabricAPITest.groovy @@ -51,8 +51,8 @@ class FabricAPITest extends Specification implements GradleProjectTestTrait { // Set the version to something constant gradle.buildGradle.text = gradle.buildGradle.text.replace('project.version + "+" + (ENV.GITHUB_RUN_NUMBER ? "" : "local-") + getBranch()', "\"$API_VERSION\"") - .replace('id "fabric-loom" version "0.9.50"', 'id "dev.architectury.loom"') - .replace('"fabric-loom"', '"dev.architectury.loom"') + .replace('id "fabric-loom" version "0.9.50"', 'id "gg.essential.loom"') + .replace('"fabric-loom"', '"gg.essential.loom"') def server = ServerRunner.create(gradle.projectDir, "1.17.1") .withMod(gradle.getOutputFile("fabric-api-${API_VERSION}.jar")) diff --git a/src/test/resources/projects/accesswidener/build.gradle b/src/test/resources/projects/accesswidener/build.gradle index bff5b7e4e..c2485394f 100644 --- a/src/test/resources/projects/accesswidener/build.gradle +++ b/src/test/resources/projects/accesswidener/build.gradle @@ -1,5 +1,5 @@ plugins { - id 'dev.architectury.loom' + id 'gg.essential.loom' id 'maven-publish' } diff --git a/src/test/resources/projects/decompile/build.gradle b/src/test/resources/projects/decompile/build.gradle index bd5d5bcae..24fcfff0a 100644 --- a/src/test/resources/projects/decompile/build.gradle +++ b/src/test/resources/projects/decompile/build.gradle @@ -1,5 +1,5 @@ plugins { - id 'dev.architectury.loom' + id 'gg.essential.loom' } dependencies { diff --git a/src/test/resources/projects/dependencyResolutionManagement/basic/build.gradle b/src/test/resources/projects/dependencyResolutionManagement/basic/build.gradle index 3e70aeac3..81cff2ed6 100644 --- a/src/test/resources/projects/dependencyResolutionManagement/basic/build.gradle +++ b/src/test/resources/projects/dependencyResolutionManagement/basic/build.gradle @@ -1,5 +1,5 @@ plugins { - id 'dev.architectury.loom' + id 'gg.essential.loom' id 'maven-publish' } diff --git a/src/test/resources/projects/dependencyResolutionManagement/projmap/build.gradle b/src/test/resources/projects/dependencyResolutionManagement/projmap/build.gradle index 0dbb633a7..3e1125723 100644 --- a/src/test/resources/projects/dependencyResolutionManagement/projmap/build.gradle +++ b/src/test/resources/projects/dependencyResolutionManagement/projmap/build.gradle @@ -1,5 +1,5 @@ plugins { - id 'dev.architectury.loom' + id 'gg.essential.loom' id 'maven-publish' } diff --git a/src/test/resources/projects/dependencyResolutionManagement/settings.gradle b/src/test/resources/projects/dependencyResolutionManagement/settings.gradle index 811c3988c..5e1ebe9ab 100644 --- a/src/test/resources/projects/dependencyResolutionManagement/settings.gradle +++ b/src/test/resources/projects/dependencyResolutionManagement/settings.gradle @@ -1,11 +1,11 @@ pluginManagement { plugins { - id 'dev.architectury.loom' + id 'gg.essential.loom' } } plugins { - id 'dev.architectury.loom' + id 'gg.essential.loom' } dependencyResolutionManagement { diff --git a/src/test/resources/projects/forge/aw2At/build.gradle b/src/test/resources/projects/forge/aw2At/build.gradle index 81ff4b9bd..56d04a9aa 100644 --- a/src/test/resources/projects/forge/aw2At/build.gradle +++ b/src/test/resources/projects/forge/aw2At/build.gradle @@ -1,5 +1,5 @@ plugins { - id 'dev.architectury.loom' + id 'gg.essential.loom' id 'maven-publish' } diff --git a/src/test/resources/projects/forge/legacyAw2AtMojmap/build.gradle b/src/test/resources/projects/forge/legacyAw2AtMojmap/build.gradle index 1b319a1bb..aa3934cfb 100644 --- a/src/test/resources/projects/forge/legacyAw2AtMojmap/build.gradle +++ b/src/test/resources/projects/forge/legacyAw2AtMojmap/build.gradle @@ -1,5 +1,5 @@ plugins { - id 'dev.architectury.loom' + id 'gg.essential.loom' id 'maven-publish' } diff --git a/src/test/resources/projects/forge/legacyAw2AtYarn/build.gradle b/src/test/resources/projects/forge/legacyAw2AtYarn/build.gradle index 3e532cb5c..1ebb085b2 100644 --- a/src/test/resources/projects/forge/legacyAw2AtYarn/build.gradle +++ b/src/test/resources/projects/forge/legacyAw2AtYarn/build.gradle @@ -1,5 +1,5 @@ plugins { - id 'dev.architectury.loom' + id 'gg.essential.loom' id 'maven-publish' } diff --git a/src/test/resources/projects/forge/simple/build.gradle b/src/test/resources/projects/forge/simple/build.gradle index dcee84dc3..f48241557 100644 --- a/src/test/resources/projects/forge/simple/build.gradle +++ b/src/test/resources/projects/forge/simple/build.gradle @@ -1,5 +1,5 @@ plugins { - id 'dev.architectury.loom' + id 'gg.essential.loom' id 'maven-publish' } diff --git a/src/test/resources/projects/java16/build.gradle b/src/test/resources/projects/java16/build.gradle index 582d5feab..556b89744 100644 --- a/src/test/resources/projects/java16/build.gradle +++ b/src/test/resources/projects/java16/build.gradle @@ -1,5 +1,5 @@ plugins { - id 'dev.architectury.loom' + id 'gg.essential.loom' id 'maven-publish' } diff --git a/src/test/resources/projects/kotlin/build.gradle.kts b/src/test/resources/projects/kotlin/build.gradle.kts index b248cb062..84e2a5048 100644 --- a/src/test/resources/projects/kotlin/build.gradle.kts +++ b/src/test/resources/projects/kotlin/build.gradle.kts @@ -2,7 +2,7 @@ import java.util.Properties plugins { kotlin("jvm") version "1.5.21" - id("dev.architectury.loom") + id("gg.essential.loom") } java { diff --git a/src/test/resources/projects/legacy/build.gradle b/src/test/resources/projects/legacy/build.gradle index 9e690447a..83084598f 100644 --- a/src/test/resources/projects/legacy/build.gradle +++ b/src/test/resources/projects/legacy/build.gradle @@ -1,5 +1,5 @@ plugins { - id 'dev.architectury.loom' + id 'gg.essential.loom' } sourceCompatibility = JavaVersion.VERSION_1_8 diff --git a/src/test/resources/projects/localFileDependency/build.gradle b/src/test/resources/projects/localFileDependency/build.gradle index 943bf1088..e977cd76c 100644 --- a/src/test/resources/projects/localFileDependency/build.gradle +++ b/src/test/resources/projects/localFileDependency/build.gradle @@ -1,5 +1,5 @@ plugins { - id 'dev.architectury.loom' + id 'gg.essential.loom' id 'maven-publish' } diff --git a/src/test/resources/projects/maven/build.gradle b/src/test/resources/projects/maven/build.gradle index 98613ab5d..745b2b431 100644 --- a/src/test/resources/projects/maven/build.gradle +++ b/src/test/resources/projects/maven/build.gradle @@ -1,5 +1,5 @@ plugins { - id 'dev.architectury.loom' + id 'gg.essential.loom' id 'maven-publish' } diff --git a/src/test/resources/projects/mavenLibrary/build.gradle b/src/test/resources/projects/mavenLibrary/build.gradle index 1573005a8..707a94076 100644 --- a/src/test/resources/projects/mavenLibrary/build.gradle +++ b/src/test/resources/projects/mavenLibrary/build.gradle @@ -1,5 +1,5 @@ plugins { - id 'dev.architectury.loom' + id 'gg.essential.loom' id 'maven-publish' } diff --git a/src/test/resources/projects/minimalBase/build.gradle b/src/test/resources/projects/minimalBase/build.gradle index b42aa7ff4..473d73aa8 100644 --- a/src/test/resources/projects/minimalBase/build.gradle +++ b/src/test/resources/projects/minimalBase/build.gradle @@ -1,7 +1,7 @@ // This is used by a range of tests that append to this file before running the gradle tasks. // Can be used for tests that require minimal custom setup plugins { - id 'dev.architectury.loom' + id 'gg.essential.loom' id 'maven-publish' } diff --git a/src/test/resources/projects/mixinApAutoRefmap/build.gradle b/src/test/resources/projects/mixinApAutoRefmap/build.gradle index df55bc401..71611866b 100644 --- a/src/test/resources/projects/mixinApAutoRefmap/build.gradle +++ b/src/test/resources/projects/mixinApAutoRefmap/build.gradle @@ -1,5 +1,5 @@ plugins { - id 'dev.architectury.loom' + id 'gg.essential.loom' id 'com.github.johnrengelman.shadow' version '7.0.0' id 'maven-publish' } diff --git a/src/test/resources/projects/mixinApSimple/build.gradle b/src/test/resources/projects/mixinApSimple/build.gradle index 72f51f639..1a1178109 100644 --- a/src/test/resources/projects/mixinApSimple/build.gradle +++ b/src/test/resources/projects/mixinApSimple/build.gradle @@ -1,5 +1,5 @@ plugins { - id 'dev.architectury.loom' + id 'gg.essential.loom' id 'maven-publish' } diff --git a/src/test/resources/projects/mojangMappings/build.gradle b/src/test/resources/projects/mojangMappings/build.gradle index 648751284..396756576 100644 --- a/src/test/resources/projects/mojangMappings/build.gradle +++ b/src/test/resources/projects/mojangMappings/build.gradle @@ -1,5 +1,5 @@ plugins { - id 'dev.architectury.loom' + id 'gg.essential.loom' id 'maven-publish' } diff --git a/src/test/resources/projects/multiproject/build.gradle b/src/test/resources/projects/multiproject/build.gradle index cebbd2af7..6c03a8dbe 100644 --- a/src/test/resources/projects/multiproject/build.gradle +++ b/src/test/resources/projects/multiproject/build.gradle @@ -1,11 +1,11 @@ plugins { id "java" id "java-library" - id 'dev.architectury.loom' apply false + id 'gg.essential.loom' apply false } allprojects { - apply plugin: "dev.architectury.loom" + apply plugin: "gg.essential.loom" version = "1.0.0" diff --git a/src/test/resources/projects/parchment/build.gradle b/src/test/resources/projects/parchment/build.gradle index 5484a2fb8..11386a835 100644 --- a/src/test/resources/projects/parchment/build.gradle +++ b/src/test/resources/projects/parchment/build.gradle @@ -1,5 +1,5 @@ plugins { - id 'dev.architectury.loom' + id 'gg.essential.loom' } repositories { diff --git a/src/test/resources/projects/reproducible/build.gradle b/src/test/resources/projects/reproducible/build.gradle index 0a19d31ec..69884f050 100644 --- a/src/test/resources/projects/reproducible/build.gradle +++ b/src/test/resources/projects/reproducible/build.gradle @@ -1,5 +1,5 @@ plugins { - id 'dev.architectury.loom' + id 'gg.essential.loom' id 'maven-publish' } diff --git a/src/test/resources/projects/runconfigs/build.gradle b/src/test/resources/projects/runconfigs/build.gradle index c4a6d3f5d..337be50de 100644 --- a/src/test/resources/projects/runconfigs/build.gradle +++ b/src/test/resources/projects/runconfigs/build.gradle @@ -1,5 +1,5 @@ plugins { - id 'dev.architectury.loom' + id 'gg.essential.loom' } loom { diff --git a/src/test/resources/projects/signed/build.gradle b/src/test/resources/projects/signed/build.gradle index 980e84e17..9d96a56f3 100644 --- a/src/test/resources/projects/signed/build.gradle +++ b/src/test/resources/projects/signed/build.gradle @@ -1,5 +1,5 @@ plugins { - id 'dev.architectury.loom' + id 'gg.essential.loom' id 'maven-publish' id 'signing' } diff --git a/src/test/resources/projects/simple/build.gradle b/src/test/resources/projects/simple/build.gradle index 2201339c3..eadd3fe36 100644 --- a/src/test/resources/projects/simple/build.gradle +++ b/src/test/resources/projects/simple/build.gradle @@ -1,5 +1,5 @@ plugins { - id 'dev.architectury.loom' + id 'gg.essential.loom' id 'maven-publish' } diff --git a/src/test/resources/projects/transitiveAccesswidener/build.gradle b/src/test/resources/projects/transitiveAccesswidener/build.gradle index 24118140f..41646d5e5 100644 --- a/src/test/resources/projects/transitiveAccesswidener/build.gradle +++ b/src/test/resources/projects/transitiveAccesswidener/build.gradle @@ -1,7 +1,7 @@ // This is used by a range of tests that append to this file before running the gradle tasks. // Can be used for tests that require minimal custom setup plugins { - id 'dev.architectury.loom' + id 'gg.essential.loom' id 'maven-publish' } diff --git a/src/test/resources/projects/unpick/build.gradle b/src/test/resources/projects/unpick/build.gradle index a0d8b2234..21b8da1e9 100644 --- a/src/test/resources/projects/unpick/build.gradle +++ b/src/test/resources/projects/unpick/build.gradle @@ -1,5 +1,5 @@ plugins { - id 'dev.architectury.loom' + id 'gg.essential.loom' } dependencies { From c9c2765a11b5b80ff30ebb4b3e42d0a34fb67c2c Mon Sep 17 00:00:00 2001 From: Jonas Herzig Date: Tue, 22 Feb 2022 13:44:13 +0100 Subject: [PATCH 17/44] Fix missing SideOnly annotations We've assumed that Forge adds its annotation everywhere via its patches but it actually only adds them if a given file needs to have a patch applied for reasons other than just the SideOnly annotation. This would leave some classes/fields/methods without annotation which resulted in an exception when deserializing horses on 1.12.2 (because it references a class which in turn has methods referencing the dedicated server class which is server-only). Instead, we now convert all Fabric annotations (added during merging) to the Forge ones and remove duplicates (where patches also added them). --- .../fg2/MinecraftLegacyPatchedProvider.java | 72 ++++++++++++++----- 1 file changed, 55 insertions(+), 17 deletions(-) diff --git a/src/main/java/net/fabricmc/loom/configuration/providers/forge/fg2/MinecraftLegacyPatchedProvider.java b/src/main/java/net/fabricmc/loom/configuration/providers/forge/fg2/MinecraftLegacyPatchedProvider.java index e74e463e3..3e931862d 100644 --- a/src/main/java/net/fabricmc/loom/configuration/providers/forge/fg2/MinecraftLegacyPatchedProvider.java +++ b/src/main/java/net/fabricmc/loom/configuration/providers/forge/fg2/MinecraftLegacyPatchedProvider.java @@ -223,8 +223,8 @@ private void mergeJars(Logger logger) throws Exception { } // The JarMerger adds Sided annotations but so do the Forge patches. The latter doesn't require extra - // dependencies beyond Forge, so we'll keep those and remove the Fabric ones. - modifyClasses(minecraftMergedPatchedJar, FabricSideStripper::new); + // dependencies beyond Forge, so we'll keep those and convert any non-redundant Fabric ones. + modifyClasses(minecraftMergedPatchedJar, SideAnnotationMerger::new); logger.info(":merged jars in " + stopwatch); } @@ -334,17 +334,30 @@ public void visitAnnotableParameterCount(int parameterCount, boolean visible) { } } - private static class FabricSideStripper extends ClassVisitor { - private static final String SIDED_DESCRIPTOR = "Lnet/fabricmc/api/Environment;"; + private static class SideAnnotationMerger extends ClassVisitor { + private static final String FABRIC_ANNOTATION_DESCRIPTOR = "Lnet/fabricmc/api/Environment;"; + private static final String FORGE_ANNOTATION_DESCRIPTOR = "Lnet/minecraftforge/fml/relauncher/SideOnly;"; + private static final String FORGE_SIDE_DESCRIPTOR = "Lnet/minecraftforge/fml/relauncher/Side;"; - private FabricSideStripper(ClassVisitor classVisitor) { + private static boolean isSideAnnotation(String descriptor) { + return FABRIC_ANNOTATION_DESCRIPTOR.equals(descriptor) || FORGE_ANNOTATION_DESCRIPTOR.equals(descriptor); + } + + private boolean visitedAnnotation; + + private SideAnnotationMerger(ClassVisitor classVisitor) { super(Opcodes.ASM9, classVisitor); } @Override public AnnotationVisitor visitAnnotation(String descriptor, boolean visible) { - if (descriptor.equals(SIDED_DESCRIPTOR)) { - return null; + if (isSideAnnotation(descriptor)) { + if (visitedAnnotation) { + return null; + } + + visitedAnnotation = true; + return new FabricToForgeConverter(super.visitAnnotation(FORGE_ANNOTATION_DESCRIPTOR, true)); } return super.visitAnnotation(descriptor, visible); @@ -352,42 +365,67 @@ public AnnotationVisitor visitAnnotation(String descriptor, boolean visible) { @Override public FieldVisitor visitField(int access, String name, String descriptor, String signature, Object value) { - return new FieldStripper(super.visitField(access, name, descriptor, signature, value)); + return new FieldSideAnnotationMerger(super.visitField(access, name, descriptor, signature, value)); } @Override public MethodVisitor visitMethod(int access, String name, String descriptor, String signature, String[] exceptions) { - return new MethodStripper(super.visitMethod(access, name, descriptor, signature, exceptions)); + return new MethodSideAnnotationMerger(super.visitMethod(access, name, descriptor, signature, exceptions)); } - private static class FieldStripper extends FieldVisitor { - private FieldStripper(FieldVisitor fieldVisitor) { + private static class FieldSideAnnotationMerger extends FieldVisitor { + private boolean visitedAnnotation; + + private FieldSideAnnotationMerger(FieldVisitor fieldVisitor) { super(Opcodes.ASM9, fieldVisitor); } @Override public AnnotationVisitor visitAnnotation(String descriptor, boolean visible) { - if (descriptor.equals(SIDED_DESCRIPTOR)) { - return null; + if (isSideAnnotation(descriptor)) { + if (visitedAnnotation) { + return null; + } + + visitedAnnotation = true; + return new FabricToForgeConverter(super.visitAnnotation(FORGE_ANNOTATION_DESCRIPTOR, true)); } return super.visitAnnotation(descriptor, visible); } } - private static class MethodStripper extends MethodVisitor { - private MethodStripper(MethodVisitor methodVisitor) { + private static class MethodSideAnnotationMerger extends MethodVisitor { + private boolean visitedAnnotation; + + private MethodSideAnnotationMerger(MethodVisitor methodVisitor) { super(Opcodes.ASM9, methodVisitor); } @Override public AnnotationVisitor visitAnnotation(String descriptor, boolean visible) { - if (descriptor.equals(SIDED_DESCRIPTOR)) { - return null; + if (isSideAnnotation(descriptor)) { + if (visitedAnnotation) { + return null; + } + + visitedAnnotation = true; + return new FabricToForgeConverter(super.visitAnnotation(FORGE_ANNOTATION_DESCRIPTOR, true)); } return super.visitAnnotation(descriptor, visible); } } + + private static class FabricToForgeConverter extends AnnotationVisitor { + private FabricToForgeConverter(AnnotationVisitor annotationVisitor) { + super(Opcodes.ASM9, annotationVisitor); + } + + @Override + public void visitEnum(String name, String descriptor, String value) { + super.visitEnum(name, FORGE_SIDE_DESCRIPTOR, value); + } + } } } From 788fa35a7b2ee87eff2574c1bcae2a30aa68ac22 Mon Sep 17 00:00:00 2001 From: Jonas Herzig Date: Tue, 22 Feb 2022 13:50:50 +0100 Subject: [PATCH 18/44] Bump loom patch version So jars (and in particular legacy forge ones) get regenerated (required because of c9c2765). --- .../configuration/providers/forge/MinecraftPatchedProvider.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/net/fabricmc/loom/configuration/providers/forge/MinecraftPatchedProvider.java b/src/main/java/net/fabricmc/loom/configuration/providers/forge/MinecraftPatchedProvider.java index f11907dc2..be4cbb589 100644 --- a/src/main/java/net/fabricmc/loom/configuration/providers/forge/MinecraftPatchedProvider.java +++ b/src/main/java/net/fabricmc/loom/configuration/providers/forge/MinecraftPatchedProvider.java @@ -101,7 +101,7 @@ public class MinecraftPatchedProvider extends DependencyProvider { private static final String LOOM_PATCH_VERSION_KEY = "Loom-Patch-Version"; - private static final String CURRENT_LOOM_PATCH_VERSION = "5"; + private static final String CURRENT_LOOM_PATCH_VERSION = "5+essential.1"; private static final String NAME_MAPPING_SERVICE_PATH = "/inject/META-INF/services/cpw.mods.modlauncher.api.INameMappingService"; // Step 1: Remap Minecraft to SRG (global) From f7b49161eae439908e8beae44344c04c7949ede9 Mon Sep 17 00:00:00 2001 From: Jonas Herzig Date: Tue, 15 Mar 2022 11:42:06 +0100 Subject: [PATCH 19/44] Make legacy Forge discover and load coremods from the classpath --- .../fg2/MinecraftLegacyPatchedProvider.java | 15 ++ .../CoreModManagerTransformer.java | 238 ++++++++++++++++++ 2 files changed, 253 insertions(+) create mode 100644 src/main/java/net/fabricmc/loom/util/legacyforge/CoreModManagerTransformer.java diff --git a/src/main/java/net/fabricmc/loom/configuration/providers/forge/fg2/MinecraftLegacyPatchedProvider.java b/src/main/java/net/fabricmc/loom/configuration/providers/forge/fg2/MinecraftLegacyPatchedProvider.java index 3e931862d..49dd48f0e 100644 --- a/src/main/java/net/fabricmc/loom/configuration/providers/forge/fg2/MinecraftLegacyPatchedProvider.java +++ b/src/main/java/net/fabricmc/loom/configuration/providers/forge/fg2/MinecraftLegacyPatchedProvider.java @@ -64,9 +64,11 @@ import net.fabricmc.loom.configuration.providers.forge.MinecraftPatchedProvider; import net.fabricmc.loom.configuration.providers.forge.PatchProvider; import net.fabricmc.loom.util.FileSystemUtil; +import net.fabricmc.loom.util.Pair; import net.fabricmc.loom.util.ThreadingUtils; import net.fabricmc.loom.util.TinyRemapperHelper; import net.fabricmc.loom.util.ZipUtils; +import net.fabricmc.loom.util.legacyforge.CoreModManagerTransformer; import net.fabricmc.loom.util.srg.AccessTransformSetMapper; import net.fabricmc.lorenztiny.TinyMappingsReader; import net.fabricmc.mappingio.tree.MappingTree; @@ -186,6 +188,19 @@ private void patchForge(Logger logger) throws Exception { walkFileSystems(log4jBeta9.toFile(), forgeJar, isHelper, this::copyReplacing); } + // While Forge will discover mods on the classpath, it won't do the same for ATs, coremods or tweakers. + // ForgeGradle "solves" this problem using a huge, gross hack (GradleForgeHacks and related classes), and only + // for ATs and coremods, not tweakers. + // No clue why FG went the hack route when it's the same project and they could have just added first-party + // support for loading both from the classpath right into Forge (it's even really simply to do). + // We'll have none of those hacks and instead patch first-party support into Forge. + ZipUtils.transform(forgeJar.toPath(), Stream.of(new Pair<>(CoreModManagerTransformer.FILE, original -> { + ClassReader reader = new ClassReader(original); + ClassWriter writer = new ClassWriter(reader, 0); + reader.accept(new CoreModManagerTransformer(writer), 0); + return writer.toByteArray(); + }))); + logger.lifecycle(":patched forge in " + stopwatch.stop()); } diff --git a/src/main/java/net/fabricmc/loom/util/legacyforge/CoreModManagerTransformer.java b/src/main/java/net/fabricmc/loom/util/legacyforge/CoreModManagerTransformer.java new file mode 100644 index 000000000..e3ee932a4 --- /dev/null +++ b/src/main/java/net/fabricmc/loom/util/legacyforge/CoreModManagerTransformer.java @@ -0,0 +1,238 @@ +package net.fabricmc.loom.util.legacyforge; + +import static org.objectweb.asm.Opcodes.ACC_PRIVATE; +import static org.objectweb.asm.Opcodes.ACC_STATIC; +import static org.objectweb.asm.Opcodes.ALOAD; +import static org.objectweb.asm.Opcodes.ASTORE; +import static org.objectweb.asm.Opcodes.BIPUSH; +import static org.objectweb.asm.Opcodes.CHECKCAST; +import static org.objectweb.asm.Opcodes.DUP; +import static org.objectweb.asm.Opcodes.GOTO; +import static org.objectweb.asm.Opcodes.ICONST_0; +import static org.objectweb.asm.Opcodes.IFEQ; +import static org.objectweb.asm.Opcodes.IFNONNULL; +import static org.objectweb.asm.Opcodes.INVOKEINTERFACE; +import static org.objectweb.asm.Opcodes.INVOKESPECIAL; +import static org.objectweb.asm.Opcodes.INVOKESTATIC; +import static org.objectweb.asm.Opcodes.INVOKEVIRTUAL; +import static org.objectweb.asm.Opcodes.NEW; +import static org.objectweb.asm.Opcodes.POP; +import static org.objectweb.asm.Opcodes.RETURN; + +import org.objectweb.asm.ClassVisitor; +import org.objectweb.asm.Label; +import org.objectweb.asm.MethodVisitor; +import org.objectweb.asm.Opcodes; + +/** + * Transforms Forge's CoreModManager class to search the classpath for coremods. + * For motivation, see comments at usage site. + */ +public class CoreModManagerTransformer extends ClassVisitor { + private static final String CLASS = "net/minecraftforge/fml/relauncher/CoreModManager"; + public static final String FILE = CLASS + ".class"; + + private static final String TARGET_METHOD = "discoverCoreMods"; + private static final String OUR_METHOD_NAME = "loom$injectCoremodsFromClasspath"; + private static final String OUR_METHOD_DESCRIPTOR = "(Lnet/minecraft/launchwrapper/LaunchClassLoader;)V"; + + public CoreModManagerTransformer(ClassVisitor classVisitor) { + super(Opcodes.ASM9, classVisitor); + } + + @Override + public MethodVisitor visitMethod(int access, String name, String descriptor, String signature, String[] exceptions) { + MethodVisitor methodVisitor = super.visitMethod(access, name, descriptor, signature, exceptions); + + // We inject a call to our method, which will discover and load coremods from the classpath, at the very start of the + // regular discovery method. + if (name.equals(TARGET_METHOD)) { + methodVisitor = new InjectCallAtHead(methodVisitor); + } + + return methodVisitor; + } + + @Override + public void visitEnd() { + // We add the following method, which will find all coremods on the classpath, and load them. + // + // private static void loom$injectCoremodsFromClasspath(LaunchClassLoader classLoader) throws Exception { + // Enumeration urls = classLoader.getResources("META-INF/MANIFEST.MF"); + // while (urls.hasMoreElements()) { + // URL url = urls.nextElement(); + // InputStream stream = url.openStream(); + // Manifest manifest = new Manifest(stream); + // stream.close(); + // String coreModClass = manifest.getMainAttributes().getValue("FMLCorePlugin"); + // if (coreModClass == null) continue; + // File file; + // if ("jar".equals(url.getProtocol())) { + // file = new File(new URL(url.getPath()).toURI()); + // } else if ("file".equals(url.getProtocol())) { + // file = new File(url.toURI()).getParentFile().getParentFile(); + // } else { + // continue; + // } + // loadCoreMod(classLoader, coreModClass, file); + // } + // } + // + // Converted to ASM via the "ASM Bytecode Viewer" IntelliJ plugin: + { + MethodVisitor methodVisitor = super.visitMethod(ACC_PRIVATE | ACC_STATIC, OUR_METHOD_NAME, OUR_METHOD_DESCRIPTOR, null, null); + methodVisitor.visitCode(); + Label label0 = new Label(); + methodVisitor.visitLabel(label0); + methodVisitor.visitLineNumber(25, label0); + methodVisitor.visitVarInsn(ALOAD, 0); + methodVisitor.visitLdcInsn("META-INF/MANIFEST.MF"); + methodVisitor.visitMethodInsn(INVOKEVIRTUAL, "net/minecraft/launchwrapper/LaunchClassLoader", "getResources", "(Ljava/lang/String;)Ljava/util/Enumeration;", false); + methodVisitor.visitVarInsn(ASTORE, 1); + Label label1 = new Label(); + methodVisitor.visitLabel(label1); + methodVisitor.visitLineNumber(26, label1); + methodVisitor.visitFrame(Opcodes.F_APPEND, 1, new Object[]{"java/util/Enumeration"}, 0, null); + methodVisitor.visitVarInsn(ALOAD, 1); + methodVisitor.visitMethodInsn(INVOKEINTERFACE, "java/util/Enumeration", "hasMoreElements", "()Z", true); + Label label2 = new Label(); + methodVisitor.visitJumpInsn(IFEQ, label2); + Label label3 = new Label(); + methodVisitor.visitLabel(label3); + methodVisitor.visitLineNumber(27, label3); + methodVisitor.visitVarInsn(ALOAD, 1); + methodVisitor.visitMethodInsn(INVOKEINTERFACE, "java/util/Enumeration", "nextElement", "()Ljava/lang/Object;", true); + methodVisitor.visitTypeInsn(CHECKCAST, "java/net/URL"); + methodVisitor.visitVarInsn(ASTORE, 2); + Label label4 = new Label(); + methodVisitor.visitLabel(label4); + methodVisitor.visitLineNumber(28, label4); + methodVisitor.visitVarInsn(ALOAD, 2); + methodVisitor.visitMethodInsn(INVOKEVIRTUAL, "java/net/URL", "openStream", "()Ljava/io/InputStream;", false); + methodVisitor.visitVarInsn(ASTORE, 3); + Label label5 = new Label(); + methodVisitor.visitLabel(label5); + methodVisitor.visitLineNumber(29, label5); + methodVisitor.visitTypeInsn(NEW, "java/util/jar/Manifest"); + methodVisitor.visitInsn(DUP); + methodVisitor.visitVarInsn(ALOAD, 3); + methodVisitor.visitMethodInsn(INVOKESPECIAL, "java/util/jar/Manifest", "", "(Ljava/io/InputStream;)V", false); + methodVisitor.visitVarInsn(ASTORE, 4); + Label label6 = new Label(); + methodVisitor.visitLabel(label6); + methodVisitor.visitLineNumber(30, label6); + methodVisitor.visitVarInsn(ALOAD, 3); + methodVisitor.visitMethodInsn(INVOKEVIRTUAL, "java/io/InputStream", "close", "()V", false); + Label label7 = new Label(); + methodVisitor.visitLabel(label7); + methodVisitor.visitLineNumber(31, label7); + methodVisitor.visitVarInsn(ALOAD, 4); + methodVisitor.visitMethodInsn(INVOKEVIRTUAL, "java/util/jar/Manifest", "getMainAttributes", "()Ljava/util/jar/Attributes;", false); + methodVisitor.visitLdcInsn("FMLCorePlugin"); + methodVisitor.visitMethodInsn(INVOKEVIRTUAL, "java/util/jar/Attributes", "getValue", "(Ljava/lang/String;)Ljava/lang/String;", false); + methodVisitor.visitVarInsn(ASTORE, 5); + Label label8 = new Label(); + methodVisitor.visitLabel(label8); + methodVisitor.visitLineNumber(32, label8); + methodVisitor.visitVarInsn(ALOAD, 5); + Label label9 = new Label(); + methodVisitor.visitJumpInsn(IFNONNULL, label9); + methodVisitor.visitJumpInsn(GOTO, label1); + methodVisitor.visitLabel(label9); + methodVisitor.visitLineNumber(34, label9); + methodVisitor.visitFrame(Opcodes.F_FULL, 6, new Object[]{"net/minecraft/launchwrapper/LaunchClassLoader", "java/util/Enumeration", "java/net/URL", "java/io/InputStream", "java/util/jar/Manifest", "java/lang/String"}, 0, new Object[]{}); + methodVisitor.visitLdcInsn("jar"); + methodVisitor.visitVarInsn(ALOAD, 2); + methodVisitor.visitMethodInsn(INVOKEVIRTUAL, "java/net/URL", "getProtocol", "()Ljava/lang/String;", false); + methodVisitor.visitMethodInsn(INVOKEVIRTUAL, "java/lang/String", "equals", "(Ljava/lang/Object;)Z", false); + Label label10 = new Label(); + methodVisitor.visitJumpInsn(IFEQ, label10); + Label label11 = new Label(); + methodVisitor.visitLabel(label11); + methodVisitor.visitLineNumber(35, label11); + methodVisitor.visitTypeInsn(NEW, "java/io/File"); + methodVisitor.visitInsn(DUP); + methodVisitor.visitTypeInsn(NEW, "java/net/URL"); + methodVisitor.visitInsn(DUP); + methodVisitor.visitVarInsn(ALOAD, 2); + methodVisitor.visitMethodInsn(INVOKEVIRTUAL, "java/net/URL", "getPath", "()Ljava/lang/String;", false); + methodVisitor.visitInsn(ICONST_0); + methodVisitor.visitVarInsn(ALOAD, 2); + methodVisitor.visitMethodInsn(INVOKEVIRTUAL, "java/net/URL", "getPath", "()Ljava/lang/String;", false); + methodVisitor.visitIntInsn(BIPUSH, 33); + methodVisitor.visitMethodInsn(INVOKEVIRTUAL, "java/lang/String", "lastIndexOf", "(I)I", false); + methodVisitor.visitMethodInsn(INVOKEVIRTUAL, "java/lang/String", "substring", "(II)Ljava/lang/String;", false); + methodVisitor.visitMethodInsn(INVOKESPECIAL, "java/net/URL", "", "(Ljava/lang/String;)V", false); + methodVisitor.visitMethodInsn(INVOKEVIRTUAL, "java/net/URL", "toURI", "()Ljava/net/URI;", false); + methodVisitor.visitMethodInsn(INVOKESPECIAL, "java/io/File", "", "(Ljava/net/URI;)V", false); + methodVisitor.visitVarInsn(ASTORE, 6); + Label label12 = new Label(); + methodVisitor.visitLabel(label12); + Label label13 = new Label(); + methodVisitor.visitJumpInsn(GOTO, label13); + methodVisitor.visitLabel(label10); + methodVisitor.visitLineNumber(36, label10); + methodVisitor.visitFrame(Opcodes.F_SAME, 0, null, 0, null); + methodVisitor.visitLdcInsn("file"); + methodVisitor.visitVarInsn(ALOAD, 2); + methodVisitor.visitMethodInsn(INVOKEVIRTUAL, "java/net/URL", "getProtocol", "()Ljava/lang/String;", false); + methodVisitor.visitMethodInsn(INVOKEVIRTUAL, "java/lang/String", "equals", "(Ljava/lang/Object;)Z", false); + methodVisitor.visitJumpInsn(IFEQ, label1); + Label label14 = new Label(); + methodVisitor.visitLabel(label14); + methodVisitor.visitLineNumber(37, label14); + methodVisitor.visitTypeInsn(NEW, "java/io/File"); + methodVisitor.visitInsn(DUP); + methodVisitor.visitVarInsn(ALOAD, 2); + methodVisitor.visitMethodInsn(INVOKEVIRTUAL, "java/net/URL", "toURI", "()Ljava/net/URI;", false); + methodVisitor.visitMethodInsn(INVOKESPECIAL, "java/io/File", "", "(Ljava/net/URI;)V", false); + methodVisitor.visitMethodInsn(INVOKEVIRTUAL, "java/io/File", "getParentFile", "()Ljava/io/File;", false); + methodVisitor.visitMethodInsn(INVOKEVIRTUAL, "java/io/File", "getParentFile", "()Ljava/io/File;", false); + methodVisitor.visitVarInsn(ASTORE, 6); + methodVisitor.visitLabel(label13); + methodVisitor.visitLineNumber(41, label13); + methodVisitor.visitFrame(Opcodes.F_APPEND, 1, new Object[]{"java/io/File"}, 0, null); + methodVisitor.visitVarInsn(ALOAD, 0); + methodVisitor.visitVarInsn(ALOAD, 5); + methodVisitor.visitVarInsn(ALOAD, 6); + methodVisitor.visitMethodInsn(INVOKESTATIC, "net/minecraftforge/fml/relauncher/CoreModManager", "loadCoreMod", "(Lnet/minecraft/launchwrapper/LaunchClassLoader;Ljava/lang/String;Ljava/io/File;)Lnet/minecraftforge/fml/relauncher/CoreModManager$FMLPluginWrapper;", false); + methodVisitor.visitInsn(POP); + Label label15 = new Label(); + methodVisitor.visitLabel(label15); + methodVisitor.visitLineNumber(42, label15); + methodVisitor.visitJumpInsn(GOTO, label1); + methodVisitor.visitLabel(label2); + methodVisitor.visitLineNumber(43, label2); + methodVisitor.visitFrame(Opcodes.F_FULL, 2, new Object[]{"net/minecraft/launchwrapper/LaunchClassLoader", "java/util/Enumeration"}, 0, new Object[]{}); + methodVisitor.visitInsn(RETURN); + Label label16 = new Label(); + methodVisitor.visitLabel(label16); + methodVisitor.visitLocalVariable("file", "Ljava/io/File;", null, label12, label10, 6); + methodVisitor.visitLocalVariable("url", "Ljava/net/URL;", null, label4, label15, 2); + methodVisitor.visitLocalVariable("stream", "Ljava/io/InputStream;", null, label5, label15, 3); + methodVisitor.visitLocalVariable("manifest", "Ljava/util/jar/Manifest;", null, label6, label15, 4); + methodVisitor.visitLocalVariable("coreModClass", "Ljava/lang/String;", null, label8, label15, 5); + methodVisitor.visitLocalVariable("file", "Ljava/io/File;", null, label13, label15, 6); + methodVisitor.visitLocalVariable("classLoader", "Lnet/minecraft/launchwrapper/LaunchClassLoader;", null, label0, label16, 0); + methodVisitor.visitLocalVariable("urls", "Ljava/util/Enumeration;", "Ljava/util/Enumeration;", label1, label16, 1); + methodVisitor.visitMaxs(8, 7); + methodVisitor.visitEnd(); + } + + super.visitEnd(); + } + + private static class InjectCallAtHead extends MethodVisitor { + private InjectCallAtHead(MethodVisitor methodVisitor) { + super(Opcodes.ASM9, methodVisitor); + } + + @Override + public void visitCode() { + super.visitCode(); + + super.visitVarInsn(Opcodes.ALOAD, 1); + super.visitMethodInsn(Opcodes.INVOKESTATIC, CLASS, OUR_METHOD_NAME, OUR_METHOD_DESCRIPTOR, false); + } + } +} From c13dc23b0cad655f6df821fa6bfdd04fb93ddf1a Mon Sep 17 00:00:00 2001 From: Jonas Herzig Date: Tue, 15 Mar 2022 11:45:05 +0100 Subject: [PATCH 20/44] Bump loom patch version --- .../configuration/providers/forge/MinecraftPatchedProvider.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/net/fabricmc/loom/configuration/providers/forge/MinecraftPatchedProvider.java b/src/main/java/net/fabricmc/loom/configuration/providers/forge/MinecraftPatchedProvider.java index be4cbb589..d74238b5d 100644 --- a/src/main/java/net/fabricmc/loom/configuration/providers/forge/MinecraftPatchedProvider.java +++ b/src/main/java/net/fabricmc/loom/configuration/providers/forge/MinecraftPatchedProvider.java @@ -101,7 +101,7 @@ public class MinecraftPatchedProvider extends DependencyProvider { private static final String LOOM_PATCH_VERSION_KEY = "Loom-Patch-Version"; - private static final String CURRENT_LOOM_PATCH_VERSION = "5+essential.1"; + private static final String CURRENT_LOOM_PATCH_VERSION = "5+essential.2"; private static final String NAME_MAPPING_SERVICE_PATH = "/inject/META-INF/services/cpw.mods.modlauncher.api.INameMappingService"; // Step 1: Remap Minecraft to SRG (global) From 8d369f2013363ff64b24f43138257d7f31b37796 Mon Sep 17 00:00:00 2001 From: Jonas Herzig Date: Tue, 10 May 2022 11:22:10 +0200 Subject: [PATCH 21/44] Fixup line numbers in forge source --- .../sources/ForgeSourcesRemapper.java | 42 +++++++++++++++++++ 1 file changed, 42 insertions(+) diff --git a/src/main/java/net/fabricmc/loom/configuration/sources/ForgeSourcesRemapper.java b/src/main/java/net/fabricmc/loom/configuration/sources/ForgeSourcesRemapper.java index 5b523153a..6456f8828 100644 --- a/src/main/java/net/fabricmc/loom/configuration/sources/ForgeSourcesRemapper.java +++ b/src/main/java/net/fabricmc/loom/configuration/sources/ForgeSourcesRemapper.java @@ -28,9 +28,12 @@ import java.io.IOException; import java.io.PrintStream; import java.io.UncheckedIOException; +import java.nio.file.FileVisitResult; import java.nio.file.Files; import java.nio.file.Path; +import java.nio.file.SimpleFileVisitor; import java.nio.file.StandardOpenOption; +import java.nio.file.attribute.BasicFileAttributes; import java.util.ArrayList; import java.util.HashSet; import java.util.List; @@ -39,6 +42,7 @@ import java.util.concurrent.ConcurrentHashMap; import java.util.function.BiConsumer; import java.util.stream.Collectors; +import java.util.stream.IntStream; import org.apache.commons.io.output.NullOutputStream; import org.cadixdev.lorenz.MappingSet; @@ -229,6 +233,7 @@ private static void remapForgeSourcesInner(Project project, Path tmpInput, Path try (FileSystemUtil.Delegate outputFs = FileSystemUtil.getJarFileSystem(tmpOutput, true)) { Path outputFsRoot = outputFs.get().getPath("/"); mercury.rewrite(tmpInput, outputFsRoot); + fixupLineNumbers(tmpInput, outputFsRoot); } catch (Exception e) { project.getLogger().warn("Could not remap " + tmpInput + " fully!", e); } @@ -256,4 +261,41 @@ private static Map extractSources(List forgeInstallerSourc taskCompleter.complete(); return sources; } + + /** + * Mercury re-organizes imports during remapping, which can result in mismatching line information when debugging. + * This method works around the issue by forcefully re-aligning the output files with the input files by inserting + * empty lines or joining multiple lines into one. + */ + private static void fixupLineNumbers(Path srcRoot, Path outRoot) throws IOException { + Files.walkFileTree(srcRoot, new SimpleFileVisitor<>() { + @Override + public FileVisitResult visitFile(Path srcPath, BasicFileAttributes attrs) throws IOException { + Path outPath = outRoot.resolve(srcRoot.relativize(srcPath).toString()); + List src = Files.readAllLines(srcPath); + List out = Files.readAllLines(outPath); + int lastSrc = IntStream.range(0, src.size()).filter(i -> src.get(i).startsWith("import")).max().orElse(0); + int lastOut = IntStream.range(0, out.size()).filter(i -> out.get(i).startsWith("import")).max().orElse(0); + + if (lastSrc == lastOut) { + return FileVisitResult.CONTINUE; + } + + while (lastOut < lastSrc) { + out.add(lastOut + 1, ""); + lastOut++; + } + + while (lastSrc < lastOut && lastOut > 0) { + out.set(lastOut - 1, out.get(lastOut - 1) + out.get(lastOut)); + out.remove(lastOut); + lastOut--; + } + + Files.write(outPath, out); + + return FileVisitResult.CONTINUE; + } + }); + } } From bd1eb92b49b30e16b707c82ebccb01584069c693 Mon Sep 17 00:00:00 2001 From: Jonas Herzig Date: Tue, 10 May 2022 11:22:45 +0200 Subject: [PATCH 22/44] Fix missing legacy forge sources Forge has not published regular source artifacts for old versions, instead the sources zip is bundled within the userdev jar. Co-authored-by: DJtheRedstoner <52044242+DJtheRedstoner@users.noreply.github.com> --- .../sources/ForgeSourcesRemapper.java | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/src/main/java/net/fabricmc/loom/configuration/sources/ForgeSourcesRemapper.java b/src/main/java/net/fabricmc/loom/configuration/sources/ForgeSourcesRemapper.java index 5b523153a..c393f9978 100644 --- a/src/main/java/net/fabricmc/loom/configuration/sources/ForgeSourcesRemapper.java +++ b/src/main/java/net/fabricmc/loom/configuration/sources/ForgeSourcesRemapper.java @@ -105,11 +105,28 @@ public static void provideForgeSources(Project project, BiConsumer forgeSources = extractSources(forgeInstallerSources); project.getLogger().lifecycle(":extracted {} forge source classes", forgeSources.size()); remapSources(project, forgeSources); forgeSources.forEach(consumer); + + if (legacySourcesZip != null) { + Files.delete(legacySourcesZip); + } } private static void remapSources(Project project, Map sources) throws IOException { From d776a57784daca57b81b0dfd65abd8bf2c98f2ab Mon Sep 17 00:00:00 2001 From: Jonas Herzig Date: Tue, 10 May 2022 11:31:37 +0200 Subject: [PATCH 23/44] Switch to different cache folder so we don't fight with upstream Otherwise it'll have to re-patch the Minecraft jars every time the user switches between two otherwise unrelated projects. This will waste a bunch of disk space, but that's likely better than constantly having to re-initialize stuff. --- .../java/net/fabricmc/loom/extension/LoomFilesBaseImpl.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/net/fabricmc/loom/extension/LoomFilesBaseImpl.java b/src/main/java/net/fabricmc/loom/extension/LoomFilesBaseImpl.java index f99395eb6..011b2450a 100644 --- a/src/main/java/net/fabricmc/loom/extension/LoomFilesBaseImpl.java +++ b/src/main/java/net/fabricmc/loom/extension/LoomFilesBaseImpl.java @@ -46,7 +46,7 @@ private static File createFile(File parent, String child) { @Override public File getUserCache() { - return createFile(getGradleUserHomeDir(), "caches" + File.separator + "fabric-loom"); + return createFile(getGradleUserHomeDir(), "caches" + File.separator + "essential-loom"); } @Override From 86c3ecb7ca62a9525defb6156b3363043ee1580b Mon Sep 17 00:00:00 2001 From: Jonas Herzig Date: Sat, 21 May 2022 16:32:22 +0200 Subject: [PATCH 24/44] Just enough to allow compiling against 1.19-pre1 Upgrading asm 9.3 because apparently there's Java 19 classes in the MC jar now. Removing the `module` wrapper around dependencies because frankly I don't know why it's a thing in the first place (works fine without on all versions of UniversalCraft) and it prevents you from having two modules differing only in the classifier, which is what MC 1.19 does. Removing the throw when there are no natives, because those come bundled in jars now (this doesn't allow running in a dev env, but it's enough to compile a jar). --- build.gradle | 10 +++++----- .../providers/minecraft/MinecraftLibraryProvider.java | 2 +- .../providers/minecraft/MinecraftNativesProvider.java | 4 ---- 3 files changed, 6 insertions(+), 10 deletions(-) diff --git a/build.gradle b/build.gradle index 3406ac512..12535a150 100644 --- a/build.gradle +++ b/build.gradle @@ -77,11 +77,11 @@ dependencies { implementation ('com.google.code.gson:gson:2.8.8') implementation ('com.fasterxml.jackson.core:jackson-databind:2.12.5') implementation ('com.google.guava:guava:30.1.1-jre') - implementation ('org.ow2.asm:asm:9.2') - implementation ('org.ow2.asm:asm-analysis:9.2') - implementation ('org.ow2.asm:asm-commons:9.2') - implementation ('org.ow2.asm:asm-tree:9.2') - implementation ('org.ow2.asm:asm-util:9.2') + implementation ('org.ow2.asm:asm:9.3') + implementation ('org.ow2.asm:asm-analysis:9.3') + implementation ('org.ow2.asm:asm-commons:9.3') + implementation ('org.ow2.asm:asm-tree:9.3') + implementation ('org.ow2.asm:asm-util:9.3') implementation ('me.tongfei:progressbar:0.9.0') // game handling utils diff --git a/src/main/java/net/fabricmc/loom/configuration/providers/minecraft/MinecraftLibraryProvider.java b/src/main/java/net/fabricmc/loom/configuration/providers/minecraft/MinecraftLibraryProvider.java index 53c9412d6..617a15713 100644 --- a/src/main/java/net/fabricmc/loom/configuration/providers/minecraft/MinecraftLibraryProvider.java +++ b/src/main/java/net/fabricmc/loom/configuration/providers/minecraft/MinecraftLibraryProvider.java @@ -42,7 +42,7 @@ public void provide(MinecraftProviderImpl minecraftProvider, Project project) { for (MinecraftVersionMeta.Library library : versionInfo.libraries()) { if (library.isValidForOS() && !library.hasNatives() && library.artifact() != null) { - project.getDependencies().add(Constants.Configurations.MINECRAFT_DEPENDENCIES, project.getDependencies().module(library.name())); + project.getDependencies().add(Constants.Configurations.MINECRAFT_DEPENDENCIES, library.name()); } } } diff --git a/src/main/java/net/fabricmc/loom/configuration/providers/minecraft/MinecraftNativesProvider.java b/src/main/java/net/fabricmc/loom/configuration/providers/minecraft/MinecraftNativesProvider.java index 03904aa68..779b72ad1 100644 --- a/src/main/java/net/fabricmc/loom/configuration/providers/minecraft/MinecraftNativesProvider.java +++ b/src/main/java/net/fabricmc/loom/configuration/providers/minecraft/MinecraftNativesProvider.java @@ -112,10 +112,6 @@ private void extractNatives() throws IOException { private boolean requiresExtract() { List natives = getNatives(); - if (natives.isEmpty()) { - throw new IllegalStateException("No natives found for the current system"); - } - for (MinecraftVersionMeta.Download library : natives) { File libJarFile = library.relativeFile(jarStore); File libSha1File = new File(nativesDir, libJarFile.getName() + ".sha1"); From b043c8a4464314b4b926831526d27fe26e968f8d Mon Sep 17 00:00:00 2001 From: DJtheRedstoner <52044242+DJtheRedstoner@users.noreply.github.com> Date: Tue, 3 Jan 2023 21:57:20 -0500 Subject: [PATCH 25/44] bump fabric-mixin-compile-extensions version This ensures the mixin 0.8.5 AP is used. --- src/main/java/net/fabricmc/loom/util/Constants.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/net/fabricmc/loom/util/Constants.java b/src/main/java/net/fabricmc/loom/util/Constants.java index 752420262..ad638008a 100644 --- a/src/main/java/net/fabricmc/loom/util/Constants.java +++ b/src/main/java/net/fabricmc/loom/util/Constants.java @@ -130,7 +130,7 @@ private Dependencies() { * Constants for versions of dependencies. */ public static final class Versions { - public static final String MIXIN_COMPILE_EXTENSIONS = "0.4.6"; + public static final String MIXIN_COMPILE_EXTENSIONS = "0.5.0"; public static final String DEV_LAUNCH_INJECTOR = "0.2.1+build.8"; public static final String TERMINAL_CONSOLE_APPENDER = "1.2.0"; public static final String JETBRAINS_ANNOTATIONS = "22.0.0"; From e7b3ef407c28e9589bda28569dc5538ad9f74d68 Mon Sep 17 00:00:00 2001 From: Jonas Herzig Date: Mon, 19 Jun 2023 10:22:57 +0200 Subject: [PATCH 26/44] Please checkstyle --- checkstyle.xml | 2 ++ .../loom/configuration/CompileConfiguration.java | 3 +-- .../forge/mcpconfig/McpConfigProvider.java | 5 ++--- .../GeneratedIntermediateMappingsProvider.java | 2 ++ .../mappings/IntermediateMappingsService.java | 13 ++----------- .../loom/task/launch/GenerateDLIConfigTask.java | 1 - 6 files changed, 9 insertions(+), 17 deletions(-) diff --git a/checkstyle.xml b/checkstyle.xml index 279b740b2..796f70869 100644 --- a/checkstyle.xml +++ b/checkstyle.xml @@ -61,6 +61,8 @@ + + diff --git a/src/main/java/net/fabricmc/loom/configuration/CompileConfiguration.java b/src/main/java/net/fabricmc/loom/configuration/CompileConfiguration.java index dda2e9919..5218a2627 100644 --- a/src/main/java/net/fabricmc/loom/configuration/CompileConfiguration.java +++ b/src/main/java/net/fabricmc/loom/configuration/CompileConfiguration.java @@ -35,8 +35,6 @@ import javax.inject.Inject; -import net.fabricmc.loom.util.Constants; - import org.gradle.api.Project; import org.gradle.api.plugins.JavaPlugin; import org.gradle.api.plugins.JavaPluginExtension; @@ -79,6 +77,7 @@ import net.fabricmc.loom.configuration.sources.ForgeSourcesRemapper; import net.fabricmc.loom.extension.MixinExtension; import net.fabricmc.loom.util.Checksum; +import net.fabricmc.loom.util.Constants; import net.fabricmc.loom.util.ExceptionUtil; import net.fabricmc.loom.util.gradle.GradleUtils; import net.fabricmc.loom.util.gradle.SourceSetHelper; diff --git a/src/main/java/net/fabricmc/loom/configuration/providers/forge/mcpconfig/McpConfigProvider.java b/src/main/java/net/fabricmc/loom/configuration/providers/forge/mcpconfig/McpConfigProvider.java index 39c9b398a..8456cb4d9 100644 --- a/src/main/java/net/fabricmc/loom/configuration/providers/forge/mcpconfig/McpConfigProvider.java +++ b/src/main/java/net/fabricmc/loom/configuration/providers/forge/mcpconfig/McpConfigProvider.java @@ -25,15 +25,12 @@ package net.fabricmc.loom.configuration.providers.forge.mcpconfig; import java.io.Reader; -import java.nio.charset.StandardCharsets; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.StandardCopyOption; -import java.util.Objects; import com.google.gson.Gson; import com.google.gson.JsonObject; -import org.apache.commons.io.IOUtils; import org.gradle.api.Project; import net.fabricmc.loom.configuration.DependencyInfo; @@ -57,6 +54,7 @@ public void provide(DependencyInfo dependency) throws Exception { init(dependency.getDependency().getVersion()); if (getExtension().isLegacyForge()) { + //CHECKSTYLE:OFF String json = """ { "data": { @@ -79,6 +77,7 @@ public void provide(DependencyInfo dependency) throws Exception { "functions": {} } """; + //CHECKSTYLE:ON data = McpConfigData.fromJson(new Gson().fromJson(json, JsonObject.class)); return; diff --git a/src/main/java/net/fabricmc/loom/configuration/providers/mappings/GeneratedIntermediateMappingsProvider.java b/src/main/java/net/fabricmc/loom/configuration/providers/mappings/GeneratedIntermediateMappingsProvider.java index 56a4e5138..21f9a3c5a 100644 --- a/src/main/java/net/fabricmc/loom/configuration/providers/mappings/GeneratedIntermediateMappingsProvider.java +++ b/src/main/java/net/fabricmc/loom/configuration/providers/mappings/GeneratedIntermediateMappingsProvider.java @@ -38,9 +38,11 @@ public void provide(Path tinyMappings) throws IOException { Path tmpFolder = Files.createTempDirectory("dummy-intermediary"); Path tinyV1 = tmpFolder.resolve("intermediary-v1.tiny"); Path mergedJar = tmpFolder.resolve("merged.jar"); + try { File clientJar = minecraftProvider.getMinecraftClientJar(); File serverJar = minecraftProvider.getMinecraftServerJar(); + try (var jarMerger = new MinecraftJarMerger(clientJar, serverJar, mergedJar.toFile())) { jarMerger.enableSyntheticParamsOffset(); jarMerger.merge(); diff --git a/src/main/java/net/fabricmc/loom/configuration/providers/mappings/IntermediateMappingsService.java b/src/main/java/net/fabricmc/loom/configuration/providers/mappings/IntermediateMappingsService.java index 672f57fa7..1f03c3902 100644 --- a/src/main/java/net/fabricmc/loom/configuration/providers/mappings/IntermediateMappingsService.java +++ b/src/main/java/net/fabricmc/loom/configuration/providers/mappings/IntermediateMappingsService.java @@ -34,17 +34,11 @@ import java.util.Objects; import java.util.function.Supplier; -import com.google.common.base.Stopwatch; import com.google.common.base.Suppliers; - -import net.fabricmc.loom.util.LoggerFilter; -import net.fabricmc.mappingio.MappingReader; -import net.fabricmc.mappingio.MappingWriter; -import net.fabricmc.mappingio.format.MappingFormat; -import net.fabricmc.stitch.commands.CommandGenerateIntermediary; - import org.gradle.api.Project; import org.jetbrains.annotations.VisibleForTesting; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import net.fabricmc.loom.LoomGradleExtension; import net.fabricmc.loom.api.mappings.intermediate.IntermediateMappingsProvider; @@ -56,9 +50,6 @@ import net.fabricmc.mappingio.format.Tiny2Reader; import net.fabricmc.mappingio.tree.MemoryMappingTree; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - public final class IntermediateMappingsService implements SharedService { private static final Logger LOGGER = LoggerFactory.getLogger(IntermediateMappingsService.class); private final Path intermediaryTiny; diff --git a/src/main/java/net/fabricmc/loom/task/launch/GenerateDLIConfigTask.java b/src/main/java/net/fabricmc/loom/task/launch/GenerateDLIConfigTask.java index de32bf2cb..252258eb5 100644 --- a/src/main/java/net/fabricmc/loom/task/launch/GenerateDLIConfigTask.java +++ b/src/main/java/net/fabricmc/loom/task/launch/GenerateDLIConfigTask.java @@ -29,7 +29,6 @@ import java.nio.charset.StandardCharsets; import java.util.ArrayList; import java.util.Arrays; -import java.util.HashMap; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; From 01de247f8d38d8d1cbbdba65d15f8e1ef22a135c Mon Sep 17 00:00:00 2001 From: Jonas Herzig Date: Wed, 21 Jun 2023 15:22:58 +0200 Subject: [PATCH 27/44] Fix data race between multiple `remapJar` tasks --- .../fabricmc/loom/build/nesting/IncludedJarFactory.java | 9 +++++---- src/main/java/net/fabricmc/loom/task/RemapJarTask.java | 2 +- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/src/main/java/net/fabricmc/loom/build/nesting/IncludedJarFactory.java b/src/main/java/net/fabricmc/loom/build/nesting/IncludedJarFactory.java index 5bf01d2ad..7e0a809d2 100644 --- a/src/main/java/net/fabricmc/loom/build/nesting/IncludedJarFactory.java +++ b/src/main/java/net/fabricmc/loom/build/nesting/IncludedJarFactory.java @@ -64,10 +64,12 @@ import net.fabricmc.loom.util.fmj.FabricModJsonFactory; public final class IncludedJarFactory { + private final Task task; private final Project project; - public IncludedJarFactory(Project project) { - this.project = project; + public IncludedJarFactory(Task task) { + this.task = task; + this.project = task.getProject(); } public Provider getNestedJars(final Configuration configuration) { @@ -173,8 +175,7 @@ private File getNestableJar(final File input, final Metadata metadata) { return input; } - LoomGradleExtension extension = LoomGradleExtension.get(project); - File tempDir = new File(extension.getFiles().getUserCache(), "temp/modprocessing"); + File tempDir = new File(project.getBuildDir(), "temp/modprocessing-" + task.getName()); if (!tempDir.exists()) { tempDir.mkdirs(); diff --git a/src/main/java/net/fabricmc/loom/task/RemapJarTask.java b/src/main/java/net/fabricmc/loom/task/RemapJarTask.java index be280e438..43900699f 100644 --- a/src/main/java/net/fabricmc/loom/task/RemapJarTask.java +++ b/src/main/java/net/fabricmc/loom/task/RemapJarTask.java @@ -146,7 +146,7 @@ public RemapJarTask() { getInjectAccessWidener().convention(false); Configuration includeConfiguration = getProject().getConfigurations().getByName(Constants.Configurations.INCLUDE); - IncludedJarFactory factory = new IncludedJarFactory(getProject()); + IncludedJarFactory factory = new IncludedJarFactory(this); if (!LoomGradleExtension.get(getProject()).isForge()) { getNestedJars().from(factory.getNestedJars(includeConfiguration)); From 7a6638d7f093467658ae6b99c4d60112cb6780e4 Mon Sep 17 00:00:00 2001 From: Jonas Herzig Date: Wed, 21 Jun 2023 15:39:57 +0200 Subject: [PATCH 28/44] Remove beta log message We shouldn't be pointing people upstream for our fork and upstream was talking about removing it anyway, so we may as well. --- src/main/java/net/fabricmc/loom/LoomGradlePlugin.java | 1 - 1 file changed, 1 deletion(-) diff --git a/src/main/java/net/fabricmc/loom/LoomGradlePlugin.java b/src/main/java/net/fabricmc/loom/LoomGradlePlugin.java index 134446d6a..55cd775cb 100644 --- a/src/main/java/net/fabricmc/loom/LoomGradlePlugin.java +++ b/src/main/java/net/fabricmc/loom/LoomGradlePlugin.java @@ -88,7 +88,6 @@ public void apply(Project project) { loggedVersions.add(LOOM_VERSION); System.setProperty("loom.printed.logged", String.join(",", loggedVersions)); project.getLogger().lifecycle("Architectury Loom: " + LOOM_VERSION); - project.getLogger().lifecycle("This version of Architectury Loom is in beta! Please report any issues you encounter: https://github.com/architectury/architectury-loom/issues"); } LibraryLocationLogger.logLibraryVersions(); From d0375435a3dda7c2d80c86ce61bab90e97b1e168 Mon Sep 17 00:00:00 2001 From: Jonas Herzig Date: Wed, 21 Jun 2023 15:40:57 +0200 Subject: [PATCH 29/44] Change version log message to prevent confusion with upstream --- src/main/java/net/fabricmc/loom/LoomGradlePlugin.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/net/fabricmc/loom/LoomGradlePlugin.java b/src/main/java/net/fabricmc/loom/LoomGradlePlugin.java index 55cd775cb..880e61daa 100644 --- a/src/main/java/net/fabricmc/loom/LoomGradlePlugin.java +++ b/src/main/java/net/fabricmc/loom/LoomGradlePlugin.java @@ -87,7 +87,7 @@ public void apply(Project project) { if (!loggedVersions.contains(LOOM_VERSION)) { loggedVersions.add(LOOM_VERSION); System.setProperty("loom.printed.logged", String.join(",", loggedVersions)); - project.getLogger().lifecycle("Architectury Loom: " + LOOM_VERSION); + project.getLogger().lifecycle("Essential Loom: " + LOOM_VERSION); } LibraryLocationLogger.logLibraryVersions(); From e22afc076f539496476da7f3845f3e6ad01e467b Mon Sep 17 00:00:00 2001 From: Jonas Herzig Date: Wed, 21 Jun 2023 15:46:49 +0200 Subject: [PATCH 30/44] Disable publication of snapshot version --- build.gradle | 37 ------------------------------------- 1 file changed, 37 deletions(-) diff --git a/build.gradle b/build.gradle index bbe0c70e4..9850b3f88 100644 --- a/build.gradle +++ b/build.gradle @@ -250,45 +250,8 @@ test { } import org.gradle.util.GradleVersion -import org.w3c.dom.Document -import org.w3c.dom.Element -import org.w3c.dom.Node publishing { - publications { - if (isSnapshot) return - - // Also publish a snapshot so people can use the latest version if they wish - snapshot(MavenPublication) { publication -> - groupId project.group - artifactId project.archivesBaseName - version baseVersion + '-SNAPSHOT' - - from components.java - } - - // Manually crate the plugin marker for snapshot versions - snapshotPlugin(MavenPublication) { - groupId 'gg.essential.loom' - artifactId 'gg.essential.loom.gradle.plugin' - version baseVersion + '-SNAPSHOT' - - pom.withXml { - // Based off org.gradle.plugin.devel.plugins.MavenPluginPublishPlugin - Element root = asElement() - Document document = root.getOwnerDocument() - Node dependencies = root.appendChild(document.createElement('dependencies')) - Node dependency = dependencies.appendChild(document.createElement('dependency')) - Node groupId = dependency.appendChild(document.createElement('groupId')) - groupId.setTextContent(project.group) - Node artifactId = dependency.appendChild(document.createElement('artifactId')) - artifactId.setTextContent(project.archivesBaseName) - Node version = dependency.appendChild(document.createElement('version')) - version.setTextContent(baseVersion + '-SNAPSHOT') - } - } - } - repositories { if (project.hasProperty("nexus_user")) { maven { From bd871b929fd5560e0e89d49c9eda86d4e66c38cf Mon Sep 17 00:00:00 2001 From: DJtheRedstoner <52044242+DJtheRedstoner@users.noreply.github.com> Date: Sat, 24 Jun 2023 20:22:51 -0400 Subject: [PATCH 31/44] Disable spotless licence header checks --- build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.gradle b/build.gradle index 9850b3f88..5738a6d9b 100644 --- a/build.gradle +++ b/build.gradle @@ -171,7 +171,7 @@ java { spotless { java { - licenseHeaderFile(rootProject.file("HEADER")).yearSeparator("-") + // licenseHeaderFile(rootProject.file("HEADER")).yearSeparator("-") targetExclude("**/loom/util/DownloadUtil.java", "**/loom/util/FileSystemUtil.java") targetExclude("**/dev/architectury/**") } From 9e455a049bbf7ec89e3f795130fff97b0bc63a0c Mon Sep 17 00:00:00 2001 From: DJtheRedstoner <52044242+DJtheRedstoner@users.noreply.github.com> Date: Sat, 24 Jun 2023 20:40:45 -0400 Subject: [PATCH 32/44] Strip jar signing from legacy forge patched jars --- .../forge/MinecraftPatchedProvider.java | 2 +- .../fg2/MinecraftLegacyPatchedProvider.java | 50 ++++++++++++++++++- 2 files changed, 50 insertions(+), 2 deletions(-) diff --git a/src/main/java/net/fabricmc/loom/configuration/providers/forge/MinecraftPatchedProvider.java b/src/main/java/net/fabricmc/loom/configuration/providers/forge/MinecraftPatchedProvider.java index c85acc5a8..4cf5c7370 100644 --- a/src/main/java/net/fabricmc/loom/configuration/providers/forge/MinecraftPatchedProvider.java +++ b/src/main/java/net/fabricmc/loom/configuration/providers/forge/MinecraftPatchedProvider.java @@ -91,7 +91,7 @@ public class MinecraftPatchedProvider { private static final String LOOM_PATCH_VERSION_KEY = "Loom-Patch-Version"; - private static final String CURRENT_LOOM_PATCH_VERSION = "8"; + private static final String CURRENT_LOOM_PATCH_VERSION = "8+essential.1"; private static final String NAME_MAPPING_SERVICE_PATH = "/inject/META-INF/services/cpw.mods.modlauncher.api.INameMappingService"; protected final Project project; diff --git a/src/main/java/net/fabricmc/loom/configuration/providers/forge/fg2/MinecraftLegacyPatchedProvider.java b/src/main/java/net/fabricmc/loom/configuration/providers/forge/fg2/MinecraftLegacyPatchedProvider.java index 4e0fbc19a..9de24afe6 100644 --- a/src/main/java/net/fabricmc/loom/configuration/providers/forge/fg2/MinecraftLegacyPatchedProvider.java +++ b/src/main/java/net/fabricmc/loom/configuration/providers/forge/fg2/MinecraftLegacyPatchedProvider.java @@ -26,16 +26,24 @@ import java.io.BufferedWriter; import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; import java.io.StringReader; import java.io.StringWriter; import java.nio.charset.StandardCharsets; +import java.nio.file.FileVisitResult; import java.nio.file.Files; import java.nio.file.Path; +import java.nio.file.SimpleFileVisitor; import java.nio.file.StandardCopyOption; import java.nio.file.StandardOpenOption; +import java.nio.file.attribute.BasicFileAttributes; import java.util.Arrays; +import java.util.Iterator; import java.util.function.Function; import java.util.function.Predicate; +import java.util.jar.Attributes; +import java.util.jar.Manifest; import java.util.stream.Stream; import com.google.common.base.Stopwatch; @@ -175,9 +183,49 @@ private void patchForge(SharedServiceManager serviceManager) throws Exception { Files.copy(getExtension().getForgeUniversalProvider().getForge().toPath(), forgeJar, StandardCopyOption.REPLACE_EXISTING); - // For the development environment, we need to remove the binpatches, otherwise forge will try to re-apply them try (FileSystemUtil.Delegate fs = FileSystemUtil.getJarFileSystem(forgeJar, false)) { + // For the development environment, we need to remove the binpatches, otherwise forge will try to re-apply them Files.delete(fs.get().getPath("binpatches.pack.lzma")); + + // We also need to remove Forge's jar signature data + // Based off https://github.com/FabricMC/tiny-remapper/blob/7f341dcbf1bae754ba992f0b0f127566f347f37f/src/main/java/net/fabricmc/tinyremapper/MetaInfFixer.java + Files.walkFileTree(fs.get().getPath("META-INF"), new SimpleFileVisitor<>() { + @Override + public FileVisitResult visitFile(Path path, BasicFileAttributes attributes) throws IOException { + String fileName = path.getFileName().toString(); + + if (fileName.endsWith(".SF") || fileName.endsWith(".DSA") || fileName.endsWith(".RSA") || fileName.endsWith(".EC") || fileName.startsWith("SIG-")) { + Files.delete(path); + } else if (path.getNameCount() == 2 && fileName.equals("MANIFEST.MF")) { + Manifest manifest = new Manifest(); + + try (InputStream is = Files.newInputStream(path)) { + manifest.read(is); + } + + for (Iterator it = manifest.getEntries().values().iterator(); it.hasNext(); ) { + Attributes attrs = it.next(); + + for (Iterator it2 = attrs.keySet().iterator(); it2.hasNext(); ) { + Attributes.Name attrName = (Attributes.Name) it2.next(); + String name = attrName.toString(); + + if (name.endsWith("-Digest") || name.contains("-Digest-") || name.equals("Magic")) { + it2.remove(); + } + } + + if (attrs.isEmpty()) it.remove(); + } + + try (OutputStream os = Files.newOutputStream(path)) { + manifest.write(os); + } + } + + return FileVisitResult.CONTINUE; + } + }); } // Older versions of Forge rely on utility classes from log4j-core 2.0-beta9 but we'll upgrade the runtime to a From 04141610c8eeb1681fb407ea166a808b6cda8c70 Mon Sep 17 00:00:00 2001 From: DJtheRedstoner <52044242+DJtheRedstoner@users.noreply.github.com> Date: Sat, 24 Jun 2023 20:41:15 -0400 Subject: [PATCH 33/44] Configure publishing for Essential maven --- .github/workflows/publish.yml | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index f2b3ab578..ac8429949 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -26,6 +26,4 @@ jobs: with: java-version: 17 - name: Upload to Maven - run: ./gradlew publish --stacktrace - env: - MAVEN_PASS: ${{ secrets.MAVEN_PASS }} + run: ./gradlew publish -Pnexus_user=${{ secrets.NEXUS_USER }} -Pnexus_password=${{ secrets.NEXUS_PASSWORD }} --stacktrace From e238cb481a5232aa284d62105f504fad2bd92cd4 Mon Sep 17 00:00:00 2001 From: DJtheRedstoner <52044242+DJtheRedstoner@users.noreply.github.com> Date: Sat, 24 Jun 2023 20:56:44 -0400 Subject: [PATCH 34/44] Update plugin ID in new tests --- .../resources/projects/forge/accessTransformer/build.gradle | 2 +- .../projects/forge/externalModDependency/build.gradle | 2 +- src/test/resources/projects/forge/loggerConfig/build.gradle | 2 +- src/test/resources/projects/forge/simpleMixinAp/build.gradle | 2 +- src/test/resources/projects/forge/singleJar/build.gradle | 2 +- src/test/resources/projects/interfaceInjection/build.gradle | 2 +- src/test/resources/projects/localRuntime/build.gradle | 2 +- src/test/resources/projects/modJavadoc/build.gradle | 2 +- src/test/resources/projects/multi-mc-versions/build.gradle | 4 ++-- src/test/resources/projects/splitSources/build.gradle | 2 +- 10 files changed, 11 insertions(+), 11 deletions(-) diff --git a/src/test/resources/projects/forge/accessTransformer/build.gradle b/src/test/resources/projects/forge/accessTransformer/build.gradle index cecc62d24..dee899067 100644 --- a/src/test/resources/projects/forge/accessTransformer/build.gradle +++ b/src/test/resources/projects/forge/accessTransformer/build.gradle @@ -1,5 +1,5 @@ plugins { - id 'dev.architectury.loom' + id 'gg.essential.loom' id 'maven-publish' } diff --git a/src/test/resources/projects/forge/externalModDependency/build.gradle b/src/test/resources/projects/forge/externalModDependency/build.gradle index fede83e97..e2734f1b0 100644 --- a/src/test/resources/projects/forge/externalModDependency/build.gradle +++ b/src/test/resources/projects/forge/externalModDependency/build.gradle @@ -1,5 +1,5 @@ plugins { - id 'dev.architectury.loom' + id 'gg.essential.loom' id 'maven-publish' } diff --git a/src/test/resources/projects/forge/loggerConfig/build.gradle b/src/test/resources/projects/forge/loggerConfig/build.gradle index d6865c5e5..c0de33d10 100644 --- a/src/test/resources/projects/forge/loggerConfig/build.gradle +++ b/src/test/resources/projects/forge/loggerConfig/build.gradle @@ -1,5 +1,5 @@ plugins { - id 'dev.architectury.loom' + id 'gg.essential.loom' } base { diff --git a/src/test/resources/projects/forge/simpleMixinAp/build.gradle b/src/test/resources/projects/forge/simpleMixinAp/build.gradle index a8f550144..586fd69d0 100644 --- a/src/test/resources/projects/forge/simpleMixinAp/build.gradle +++ b/src/test/resources/projects/forge/simpleMixinAp/build.gradle @@ -1,5 +1,5 @@ plugins { - id 'dev.architectury.loom' + id 'gg.essential.loom' id 'maven-publish' } diff --git a/src/test/resources/projects/forge/singleJar/build.gradle b/src/test/resources/projects/forge/singleJar/build.gradle index 99d3cd63e..a6477a96a 100644 --- a/src/test/resources/projects/forge/singleJar/build.gradle +++ b/src/test/resources/projects/forge/singleJar/build.gradle @@ -1,5 +1,5 @@ plugins { - id 'dev.architectury.loom' + id 'gg.essential.loom' id 'maven-publish' } diff --git a/src/test/resources/projects/interfaceInjection/build.gradle b/src/test/resources/projects/interfaceInjection/build.gradle index 7d0a7c17e..27fe6ef50 100644 --- a/src/test/resources/projects/interfaceInjection/build.gradle +++ b/src/test/resources/projects/interfaceInjection/build.gradle @@ -1,7 +1,7 @@ // This is used by a range of tests that append to this file before running the gradle tasks. // Can be used for tests that require minimal custom setup plugins { - id 'dev.architectury.loom' + id 'gg.essential.loom' id 'maven-publish' } diff --git a/src/test/resources/projects/localRuntime/build.gradle b/src/test/resources/projects/localRuntime/build.gradle index bf5519d0b..a2a13648f 100644 --- a/src/test/resources/projects/localRuntime/build.gradle +++ b/src/test/resources/projects/localRuntime/build.gradle @@ -1,5 +1,5 @@ plugins { - id 'dev.architectury.loom' + id 'gg.essential.loom' id 'maven-publish' } diff --git a/src/test/resources/projects/modJavadoc/build.gradle b/src/test/resources/projects/modJavadoc/build.gradle index 986377fdc..44aaa29a0 100644 --- a/src/test/resources/projects/modJavadoc/build.gradle +++ b/src/test/resources/projects/modJavadoc/build.gradle @@ -1,7 +1,7 @@ // This is used by a range of tests that append to this file before running the gradle tasks. // Can be used for tests that require minimal custom setup plugins { - id 'dev.architectury.loom' + id 'gg.essential.loom' id 'maven-publish' } diff --git a/src/test/resources/projects/multi-mc-versions/build.gradle b/src/test/resources/projects/multi-mc-versions/build.gradle index ef8182d72..1ed63ae46 100644 --- a/src/test/resources/projects/multi-mc-versions/build.gradle +++ b/src/test/resources/projects/multi-mc-versions/build.gradle @@ -2,7 +2,7 @@ import groovy.json.JsonSlurper plugins { id "java" - id 'dev.architectury.loom' apply false + id 'gg.essential.loom' apply false } allprojects { @@ -19,7 +19,7 @@ def getMappingVersion(String mcVersion) { } subprojects { - apply plugin: "dev.architectury.loom" + apply plugin: "gg.essential.loom" base { archivesName = rootProject.name + "-" + project.name diff --git a/src/test/resources/projects/splitSources/build.gradle b/src/test/resources/projects/splitSources/build.gradle index a074a191d..be76fd008 100644 --- a/src/test/resources/projects/splitSources/build.gradle +++ b/src/test/resources/projects/splitSources/build.gradle @@ -1,5 +1,5 @@ plugins { - id 'dev.architectury.loom' version '0.12.local' + id 'gg.essential.loom' version '0.12.local' id 'maven-publish' } From e5a16fda4924ae9877faa838f945d810b34b580c Mon Sep 17 00:00:00 2001 From: DJtheRedstoner <52044242+DJtheRedstoner@users.noreply.github.com> Date: Fri, 7 Jul 2023 08:00:51 -0400 Subject: [PATCH 35/44] Disable the genForgePatchedSources task on legacy forge --- .../decompile/SingleJarDecompileConfiguration.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/net/fabricmc/loom/configuration/decompile/SingleJarDecompileConfiguration.java b/src/main/java/net/fabricmc/loom/configuration/decompile/SingleJarDecompileConfiguration.java index e8423d2ff..b5b79377c 100644 --- a/src/main/java/net/fabricmc/loom/configuration/decompile/SingleJarDecompileConfiguration.java +++ b/src/main/java/net/fabricmc/loom/configuration/decompile/SingleJarDecompileConfiguration.java @@ -86,7 +86,7 @@ public final void afterEvaluation() { }); // TODO: Support for env-only jars? - if (extension.isForge() && extension.getMinecraftJarConfiguration().get() == MinecraftJarConfiguration.MERGED) { + if (extension.isForge() && !extension.isLegacyForge() && extension.getMinecraftJarConfiguration().get() == MinecraftJarConfiguration.MERGED) { project.getTasks().register("genForgePatchedSources", GenerateForgePatchedSourcesTask.class, task -> { task.setDescription("Decompile Minecraft using Forge's toolchain."); task.setGroup(Constants.TaskGroup.FABRIC); From 70d36e69781096f5e1037c47456781766b123a20 Mon Sep 17 00:00:00 2001 From: DJtheRedstoner <52044242+DJtheRedstoner@users.noreply.github.com> Date: Thu, 27 Jul 2023 12:23:46 -0400 Subject: [PATCH 36/44] Use environment variables for secrets --- .github/workflows/publish.yml | 5 ++++- build.gradle | 6 +++--- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index ac8429949..a4fd3c2fe 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -26,4 +26,7 @@ jobs: with: java-version: 17 - name: Upload to Maven - run: ./gradlew publish -Pnexus_user=${{ secrets.NEXUS_USER }} -Pnexus_password=${{ secrets.NEXUS_PASSWORD }} --stacktrace + run: ./gradlew publish --stacktrace + env: + NEXUS_USER: ${{ secrets.NEXUS_USER }} + NEXUS_PASSWORD: {{ secrets.NEXUS_PASSWORD }} diff --git a/build.gradle b/build.gradle index 5738a6d9b..e42f6a99a 100644 --- a/build.gradle +++ b/build.gradle @@ -253,13 +253,13 @@ import org.gradle.util.GradleVersion publishing { repositories { - if (project.hasProperty("nexus_user")) { + if (!System.getenv("NEXUS_USER").isEmpty()) { maven { url = "https://repo.sk1er.club/repository/maven-releases/" name = "nexus-public" credentials { - username = project.property("nexus_user").toString() - password = project.property("nexus_password").toString() + username = System.getenv("NEXUS_USER") + password = System.getenv("NEXUS_PASSWORD") } } } From 15b41b6c7acd99d8113ba11d13e64bc6394a170c Mon Sep 17 00:00:00 2001 From: DJtheRedstoner <52044242+DJtheRedstoner@users.noreply.github.com> Date: Thu, 27 Jul 2023 12:25:49 -0400 Subject: [PATCH 37/44] lets try this again --- .github/workflows/publish.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index a4fd3c2fe..2cb78701d 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -29,4 +29,4 @@ jobs: run: ./gradlew publish --stacktrace env: NEXUS_USER: ${{ secrets.NEXUS_USER }} - NEXUS_PASSWORD: {{ secrets.NEXUS_PASSWORD }} + NEXUS_PASSWORD: ${{ secrets.NEXUS_PASSWORD }} From a3f299cdaf7ed2b828bbe8a95c9ac76a48b5cafe Mon Sep 17 00:00:00 2001 From: DJtheRedstoner <52044242+DJtheRedstoner@users.noreply.github.com> Date: Thu, 27 Jul 2023 14:18:25 -0400 Subject: [PATCH 38/44] testing123 From 1ea242d430fd86df486b3bb4be961813ca143e61 Mon Sep 17 00:00:00 2001 From: DJtheRedstoner <52044242+DJtheRedstoner@users.noreply.github.com> Date: Thu, 27 Jul 2023 14:25:11 -0400 Subject: [PATCH 39/44] testing456 From fac5c9f39f34406ab08f3e1bdbd62060472f187a Mon Sep 17 00:00:00 2001 From: DJtheRedstoner <52044242+DJtheRedstoner@users.noreply.github.com> Date: Thu, 27 Jul 2023 14:52:31 -0400 Subject: [PATCH 40/44] Fix NPE --- build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.gradle b/build.gradle index e42f6a99a..51d7092e4 100644 --- a/build.gradle +++ b/build.gradle @@ -253,7 +253,7 @@ import org.gradle.util.GradleVersion publishing { repositories { - if (!System.getenv("NEXUS_USER").isEmpty()) { + if (System.getenv("NEXUS_USER") != null) { maven { url = "https://repo.sk1er.club/repository/maven-releases/" name = "nexus-public" From d2c8a1441ba0d98c90fa7fb09fe9285cc825661f Mon Sep 17 00:00:00 2001 From: Jonas Herzig Date: Thu, 10 Aug 2023 10:56:47 +0200 Subject: [PATCH 41/44] Fix non-reproducible build due to HashSet iteration order --- src/main/java/net/fabricmc/loom/task/RemapJarTask.java | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/main/java/net/fabricmc/loom/task/RemapJarTask.java b/src/main/java/net/fabricmc/loom/task/RemapJarTask.java index bc1098307..f92baff10 100644 --- a/src/main/java/net/fabricmc/loom/task/RemapJarTask.java +++ b/src/main/java/net/fabricmc/loom/task/RemapJarTask.java @@ -31,7 +31,6 @@ import java.nio.file.Path; import java.util.ArrayList; import java.util.Collection; -import java.util.HashSet; import java.util.LinkedHashSet; import java.util.List; import java.util.Map; @@ -431,7 +430,7 @@ private void addNestedJars() { return; } - Set jars = new HashSet<>(nestedJars.getFiles()); + Set jars = new LinkedHashSet<>(nestedJars.getFiles()); jars.addAll(forgeNestedJars.get().stream().map(NestedFile::file).toList()); JarNester.nestJars(jars, forgeNestedJars.getOrElse(List.of()), outputFile.toFile(), getParameters().getPlatform().get(), LOGGER); } From 273c9c4531c1fb5fa36f361ea8ae3aa04945e25f Mon Sep 17 00:00:00 2001 From: Jonas Herzig Date: Mon, 14 Aug 2023 10:52:00 +0200 Subject: [PATCH 42/44] Fix reproducible timestamps depending on timezone See https://github.com/FabricMC/fabric-loom/issues/942 --- src/main/java/net/fabricmc/loom/util/ZipReprocessorUtil.java | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/main/java/net/fabricmc/loom/util/ZipReprocessorUtil.java b/src/main/java/net/fabricmc/loom/util/ZipReprocessorUtil.java index f1c8c3645..b1458a431 100644 --- a/src/main/java/net/fabricmc/loom/util/ZipReprocessorUtil.java +++ b/src/main/java/net/fabricmc/loom/util/ZipReprocessorUtil.java @@ -30,7 +30,6 @@ import java.io.IOException; import java.io.InputStream; import java.nio.file.Path; -import java.nio.file.attribute.FileTime; import java.util.Calendar; import java.util.Comparator; import java.util.GregorianCalendar; @@ -174,7 +173,5 @@ private static void copyZipEntry(ZipOutputStream zipOutputStream, ZipEntry entry private static void setConstantFileTime(ZipEntry entry) { entry.setTime(ZipReprocessorUtil.CONSTANT_TIME_FOR_ZIP_ENTRIES); - entry.setLastModifiedTime(FileTime.fromMillis(ZipReprocessorUtil.CONSTANT_TIME_FOR_ZIP_ENTRIES)); - entry.setLastAccessTime(FileTime.fromMillis(ZipReprocessorUtil.CONSTANT_TIME_FOR_ZIP_ENTRIES)); } } From 23378588ad3824feeb4339f0965ac3e75321b001 Mon Sep 17 00:00:00 2001 From: Jonas Herzig Date: Wed, 27 Sep 2023 09:03:22 +0200 Subject: [PATCH 43/44] Fix BuildSharedServiceManager not releasing memory during build The `BuildSharedServiceManager` is used by `RemapJarTask` and `RemapSourcesJarTask` to prevent having to load mappings and potentially TinyRemapper multiple times. And while it does seemingly have a reference counter, only the thing as a whole is reference counted, not the individual services. As such, if a build contains many projects on different MC versions, even if the mappings for one of those versions will no longer be used during the rest of the build, the manager will still hold onto them, slowly but surely consuming more and more memory as the build progresses. This commit works around the issue by creating a separate `BuildSharedServiceManager` per project (unless multiProjectOptimisation is enabled), such that once all tasks of one project have completed, that whole manager can be released. For a proper fix one would likely want to do reference counting on the SharedService level and/or use SoftReferences so cached data can be disposed of if memory becomes scarce. This workaround isn't ideal in that: - Gradle is not required to run all tasks of one project before it goes to the next one; in the worst case it could run `remapJar` of all projects before running any `remapSourcesJar` tasks, effectively voiding this workaround. In practice it seems to behave quite well though. - Projects that used to be able to share services will now need to initialize them multiple times. Given our Loom fork is primarily used to develop multi-version-heavy, not multi-project-heavy projects, this seems like an acceptable trade-off (e.g. none of our libs are able to make use of the sharing to begin with). GitHub: #9 --- .../loom/util/service/BuildSharedServiceManager.java | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/src/main/java/net/fabricmc/loom/util/service/BuildSharedServiceManager.java b/src/main/java/net/fabricmc/loom/util/service/BuildSharedServiceManager.java index 6575611de..972ac61c7 100644 --- a/src/main/java/net/fabricmc/loom/util/service/BuildSharedServiceManager.java +++ b/src/main/java/net/fabricmc/loom/util/service/BuildSharedServiceManager.java @@ -27,6 +27,7 @@ import java.util.Objects; import java.util.concurrent.atomic.AtomicInteger; +import org.gradle.api.Project; import org.gradle.api.Task; import org.gradle.api.provider.Provider; import org.gradle.api.services.BuildService; @@ -37,6 +38,8 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import net.fabricmc.loom.LoomGradleExtension; + public abstract class BuildSharedServiceManager implements BuildService { private static final Logger LOGGER = LoggerFactory.getLogger(BuildSharedServiceManager.class); private static final String NAME = "loom:sharedServiceManager"; @@ -45,7 +48,14 @@ public abstract class BuildSharedServiceManager implements BuildService createForTask(Task task, BuildEventsListenerRegistry buildEventsListenerRegistry) { - Provider provider = task.getProject().getGradle().getSharedServices().registerIfAbsent(NAME, BuildSharedServiceManager.class, spec -> { + Project project = task.getProject(); + String name = NAME; + + if (!LoomGradleExtension.get(project).multiProjectOptimisation()) { + name += "-" + project.getPath(); + } + + Provider provider = project.getGradle().getSharedServices().registerIfAbsent(name, BuildSharedServiceManager.class, spec -> { }); task.usesService(provider); From 21bfc0f7049e47214dba97cd36d9cc5cf236c2c7 Mon Sep 17 00:00:00 2001 From: DJtheRedstoner <52044242+DJtheRedstoner@users.noreply.github.com> Date: Mon, 30 Oct 2023 16:10:16 -0400 Subject: [PATCH 44/44] update readme [skip ci] --- README.md | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/README.md b/README.md index 4748337fb..a0f8d33ca 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,15 @@ +# Essential Loom + +A fork of [Architectury Loom](https://github.com/architectury/architectury-loom/), primarily to support legacy forge versions +but with some other changes for our purposes. Used by [Essential Gradle Toolkit](https://github.com/EssentialGG/essential-gradle-toolkit/). + +Talk to us on [Discord](https://discord.gg/essential). Use the `#programmer-chat` channel for Essential Loom discussions. +Issues can be reported on Discord or using GitHub issues. + +# Original Readme + +--- + # Architectury Loom Talk to us on [Discord](https://discord.gg/C2RdJDpRBP)!