From 8d9d4df65f253363c611df749dbe10f5f0ddf316 Mon Sep 17 00:00:00 2001 From: Juuz <6596629+Juuxel@users.noreply.github.com> Date: Wed, 3 Sep 2025 13:49:22 +0300 Subject: [PATCH] Fix split official namespaces existing on versions where they don't make sense (#1361) * Don't use clientOfficial and serverOfficial namespaces on versions with only one jar Fixes #1360. Renames an experimental API in IntermediateMappingsProvider: getIsLegacyMinecraft -> getUseSplitOfficialNamespaces * Add test for 0.30 with deobf mappings + no intermediate mappings * Change split official ns check to Beta 1.0..<1.3 range check * Fix javadoc * Clarify comment in MinecraftVersionMeta --- .../IntermediateMappingsProvider.java | 6 +-- .../mappings/IntermediateMappingsService.java | 8 ++-- .../NoOpIntermediateMappingsProvider.java | 4 +- .../mappings/tiny/MappingsMerger.java | 8 ++-- .../minecraft/MinecraftProvider.java | 12 +++++- .../minecraft/MinecraftVersionMeta.java | 34 +++++++++++++++- .../minecraft/SingleJarMinecraftProvider.java | 7 ++-- .../extension/LoomGradleExtensionApiImpl.java | 9 ++--- .../extension/LoomGradleExtensionImpl.java | 6 +-- .../net/fabricmc/loom/util/Constants.java | 3 +- .../test/integration/LegacyProjectTest.groovy | 39 ++++++++++++++++++- .../loom/test/unit/MappingsMergerTest.groovy | 6 +-- src/test/resources/mappings/0.30-minimal.tiny | 2 + 13 files changed, 110 insertions(+), 34 deletions(-) create mode 100644 src/test/resources/mappings/0.30-minimal.tiny diff --git a/src/main/java/net/fabricmc/loom/api/mappings/intermediate/IntermediateMappingsProvider.java b/src/main/java/net/fabricmc/loom/api/mappings/intermediate/IntermediateMappingsProvider.java index d4987b08..9593b914 100644 --- a/src/main/java/net/fabricmc/loom/api/mappings/intermediate/IntermediateMappingsProvider.java +++ b/src/main/java/net/fabricmc/loom/api/mappings/intermediate/IntermediateMappingsProvider.java @@ -1,7 +1,7 @@ /* * This file is part of fabric-loom, licensed under the MIT License (MIT). * - * Copyright (c) 2022 FabricMC + * Copyright (c) 2022-2025 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 @@ -45,12 +45,12 @@ public abstract class IntermediateMappingsProvider implements Named { public abstract Property> getDownloader(); /** - * Set to true if the minecraft version is pre 1.3. + * Set to true if the minecraft version is at least Beta 1.0 and pre 1.3. * When true the expected src namespace is intermediary, and the expected dst namespaces are clientOfficial and/or serverOfficial * When false the expected src namespace is named and the expected dst namespace is intermediary */ @ApiStatus.Experimental - public abstract Property getIsLegacyMinecraft(); + public abstract Property getUseSplitOfficialNamespaces(); /** * Generate or download a tinyv2 mapping file with intermediary and named namespaces. 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 b9edaffe..16299dae 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 @@ -1,7 +1,7 @@ /* * This file is part of fabric-loom, licensed under the MIT License (MIT). * - * Copyright (c) 2022 FabricMC + * Copyright (c) 2022-2025 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 @@ -103,9 +103,9 @@ public final class IntermediateMappingsService extends Service=1.3 + final String expectedSrcNs = minecraftProvider.isLegacySplitOfficialNamespaceVersion() + ? MappingsNamespace.INTERMEDIARY.toString() // >=beta 1.0 and <1.3 + : MappingsNamespace.OFFICIAL.toString(); // >=1.3 or { options.getIntermediaryTiny().set(intermediaryTiny.toFile()); diff --git a/src/main/java/net/fabricmc/loom/configuration/providers/mappings/NoOpIntermediateMappingsProvider.java b/src/main/java/net/fabricmc/loom/configuration/providers/mappings/NoOpIntermediateMappingsProvider.java index f1c6b997..4c65eeb8 100644 --- a/src/main/java/net/fabricmc/loom/configuration/providers/mappings/NoOpIntermediateMappingsProvider.java +++ b/src/main/java/net/fabricmc/loom/configuration/providers/mappings/NoOpIntermediateMappingsProvider.java @@ -1,7 +1,7 @@ /* * This file is part of fabric-loom, licensed under the MIT License (MIT). * - * Copyright (c) 2022 FabricMC + * Copyright (c) 2022-2025 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 @@ -42,7 +42,7 @@ public abstract class NoOpIntermediateMappingsProvider extends IntermediateMappi @Override public void provide(Path tinyMappings) throws IOException { - Files.writeString(tinyMappings, getIsLegacyMinecraft().get() ? HEADER_OFFICIAL_LEGACY_MERGED : HEADER_OFFICIAL_MERGED, StandardCharsets.UTF_8); + Files.writeString(tinyMappings, getUseSplitOfficialNamespaces().get() ? HEADER_OFFICIAL_LEGACY_MERGED : HEADER_OFFICIAL_MERGED, StandardCharsets.UTF_8); } @Override diff --git a/src/main/java/net/fabricmc/loom/configuration/providers/mappings/tiny/MappingsMerger.java b/src/main/java/net/fabricmc/loom/configuration/providers/mappings/tiny/MappingsMerger.java index 5827a437..68c4ddc2 100644 --- a/src/main/java/net/fabricmc/loom/configuration/providers/mappings/tiny/MappingsMerger.java +++ b/src/main/java/net/fabricmc/loom/configuration/providers/mappings/tiny/MappingsMerger.java @@ -1,7 +1,7 @@ /* * This file is part of fabric-loom, licensed under the MIT License (MIT). * - * Copyright (c) 2022 FabricMC + * Copyright (c) 2022-2025 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 @@ -54,8 +54,8 @@ public final class MappingsMerger { Stopwatch stopwatch = Stopwatch.createStarted(); LOGGER.info(":merging mappings"); - if (minecraftProvider.isLegacyVersion()) { - legacyMergeAndSaveMappings(from, out, intermediateMappingsService); + if (minecraftProvider.isLegacySplitOfficialNamespaceVersion()) { + legacyMergedMergeAndSaveMappings(from, out, intermediateMappingsService); } else { mergeAndSaveMappings(from, out, intermediateMappingsService); } @@ -85,7 +85,7 @@ public final class MappingsMerger { } @VisibleForTesting - public static void legacyMergeAndSaveMappings(Path from, Path out, IntermediateMappingsService intermediateMappingsService) throws IOException { + public static void legacyMergedMergeAndSaveMappings(Path from, Path out, IntermediateMappingsService intermediateMappingsService) throws IOException { MemoryMappingTree intermediaryTree = new MemoryMappingTree(); intermediateMappingsService.getMemoryMappingTree().accept(intermediaryTree); diff --git a/src/main/java/net/fabricmc/loom/configuration/providers/minecraft/MinecraftProvider.java b/src/main/java/net/fabricmc/loom/configuration/providers/minecraft/MinecraftProvider.java index 30c19c52..af4d4ecd 100644 --- a/src/main/java/net/fabricmc/loom/configuration/providers/minecraft/MinecraftProvider.java +++ b/src/main/java/net/fabricmc/loom/configuration/providers/minecraft/MinecraftProvider.java @@ -1,7 +1,7 @@ /* * This file is part of fabric-loom, licensed under the MIT License (MIT). * - * Copyright (c) 2018-2021 FabricMC + * Copyright (c) 2018-2025 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 @@ -249,7 +249,15 @@ public abstract class MinecraftProvider { * @return true if the minecraft version is older than 1.3. */ public boolean isLegacyVersion() { - return !getVersionInfo().isVersionOrNewer(Constants.RELEASE_TIME_1_3); + return getVersionInfo().isLegacyVersion(); + } + + /** + * Returns true if the minecraft version is between Beta 1.0 (inclusive) and 1.3 (exclusive), + * which splits the {@code official} mapping namespace into env-specific variants. + */ + public boolean isLegacySplitOfficialNamespaceVersion() { + return getVersionInfo().isLegacySplitOfficialNamespaceVersion(); } @Nullable diff --git a/src/main/java/net/fabricmc/loom/configuration/providers/minecraft/MinecraftVersionMeta.java b/src/main/java/net/fabricmc/loom/configuration/providers/minecraft/MinecraftVersionMeta.java index 5325cc38..2fbda51f 100644 --- a/src/main/java/net/fabricmc/loom/configuration/providers/minecraft/MinecraftVersionMeta.java +++ b/src/main/java/net/fabricmc/loom/configuration/providers/minecraft/MinecraftVersionMeta.java @@ -1,7 +1,7 @@ /* * This file is part of fabric-loom, licensed under the MIT License (MIT). * - * Copyright (c) 2021 FabricMC + * Copyright (c) 2021-2025 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 @@ -31,6 +31,7 @@ import java.util.Objects; import org.jetbrains.annotations.Nullable; +import net.fabricmc.loom.util.Constants; import net.fabricmc.loom.util.Platform; @SuppressWarnings("unused") @@ -64,6 +65,37 @@ public record MinecraftVersionMeta( return this.releaseTime().compareTo(releaseTime) >= 0; } + /** + * Returns true if the version was released before 1.3. + * This means that the client and server can't be merged normally due to different obfuscation + * or one of the environments missing. + */ + public boolean isLegacyVersion() { + return !isVersionOrNewer(Constants.RELEASE_TIME_1_3); + } + + public boolean hasClient() { + return downloads().containsKey("client"); + } + + public boolean hasServer() { + return downloads().containsKey("server"); + } + + /** + * Returns true if the version was released after Beta 1.0 (inclusive) but before 1.3 (exclusive). + * + *

This includes some versions that only have a client jar or a server jar to match behaviour + * across all versions in the range. + */ + public boolean isLegacySplitOfficialNamespaceVersion() { + // TODO: Allow "official" as the obf namespace on single-env versions in this range by checking the mappings + // to see which one they have. + // Likewise, "clientOfficial"/"serverOfficial" could be allowed older single-env releases + // as an alternative to "official". + return isLegacyVersion() && isVersionOrNewer(Constants.RELEASE_TIME_BETA_1_0); + } + public boolean hasNativesToExtract() { return libraries.stream().anyMatch(Library::hasNatives); } diff --git a/src/main/java/net/fabricmc/loom/configuration/providers/minecraft/SingleJarMinecraftProvider.java b/src/main/java/net/fabricmc/loom/configuration/providers/minecraft/SingleJarMinecraftProvider.java index ee682109..fa7caadc 100644 --- a/src/main/java/net/fabricmc/loom/configuration/providers/minecraft/SingleJarMinecraftProvider.java +++ b/src/main/java/net/fabricmc/loom/configuration/providers/minecraft/SingleJarMinecraftProvider.java @@ -1,7 +1,7 @@ /* * This file is part of fabric-loom, licensed under the MIT License (MIT). * - * Copyright (c) 2022 FabricMC + * Copyright (c) 2022-2025 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 @@ -31,7 +31,6 @@ import java.util.List; import net.fabricmc.loom.api.mappings.layered.MappingsNamespace; import net.fabricmc.loom.configuration.ConfigContext; import net.fabricmc.loom.configuration.providers.BundleMetadata; -import net.fabricmc.loom.util.Constants; import net.fabricmc.loom.util.TinyRemapperLoggerAdapter; import net.fabricmc.tinyremapper.NonClassCopyMode; import net.fabricmc.tinyremapper.OutputConsumerPath; @@ -55,8 +54,8 @@ public abstract sealed class SingleJarMinecraftProvider extends MinecraftProvide } private static MappingsNamespace getOfficialNamespace(MinecraftMetadataProvider metadataProvider, boolean server) { - // Versions before 1.3 don't have a common namespace, so use side specific namespaces. - if (!metadataProvider.getVersionMeta().isVersionOrNewer(Constants.RELEASE_TIME_1_3)) { + // Some versions before 1.3 don't have a common namespace, so use side specific namespaces. + if (metadataProvider.getVersionMeta().isLegacySplitOfficialNamespaceVersion()) { return server ? MappingsNamespace.SERVER_OFFICIAL : MappingsNamespace.CLIENT_OFFICIAL; } diff --git a/src/main/java/net/fabricmc/loom/extension/LoomGradleExtensionApiImpl.java b/src/main/java/net/fabricmc/loom/extension/LoomGradleExtensionApiImpl.java index 7099a65f..e547ac61 100644 --- a/src/main/java/net/fabricmc/loom/extension/LoomGradleExtensionApiImpl.java +++ b/src/main/java/net/fabricmc/loom/extension/LoomGradleExtensionApiImpl.java @@ -1,7 +1,7 @@ /* * This file is part of fabric-loom, licensed under the MIT License (MIT). * - * Copyright (c) 2021-2024 FabricMC + * Copyright (c) 2021-2025 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 @@ -71,7 +71,6 @@ import net.fabricmc.loom.configuration.providers.minecraft.MinecraftJarConfigura import net.fabricmc.loom.configuration.providers.minecraft.MinecraftMetadataProvider; import net.fabricmc.loom.configuration.providers.minecraft.MinecraftSourceSets; import net.fabricmc.loom.task.GenerateSourcesTask; -import net.fabricmc.loom.util.Constants; import net.fabricmc.loom.util.DeprecationHelper; import net.fabricmc.loom.util.MirrorUtil; import net.fabricmc.loom.util.fmj.FabricModJson; @@ -160,11 +159,11 @@ public abstract class LoomGradleExtensionApiImpl implements LoomGradleExtensionA // if no configuration is selected by the user, attempt to select one // based on the mc version and which sides are present for it - if (!metadataProvider.getVersionMeta().downloads().containsKey("server")) { + if (!metadataProvider.getVersionMeta().hasServer()) { return MinecraftJarConfiguration.CLIENT_ONLY; - } else if (!metadataProvider.getVersionMeta().downloads().containsKey("client")) { + } else if (!metadataProvider.getVersionMeta().hasClient()) { return MinecraftJarConfiguration.SERVER_ONLY; - } else if (metadataProvider.getVersionMeta().isVersionOrNewer(Constants.RELEASE_TIME_1_3)) { + } else if (!metadataProvider.getVersionMeta().isLegacyVersion()) { return MinecraftJarConfiguration.MERGED; } else { return MinecraftJarConfiguration.LEGACY_MERGED; diff --git a/src/main/java/net/fabricmc/loom/extension/LoomGradleExtensionImpl.java b/src/main/java/net/fabricmc/loom/extension/LoomGradleExtensionImpl.java index 8ec88fb0..40c59eb2 100644 --- a/src/main/java/net/fabricmc/loom/extension/LoomGradleExtensionImpl.java +++ b/src/main/java/net/fabricmc/loom/extension/LoomGradleExtensionImpl.java @@ -1,7 +1,7 @@ /* * This file is part of fabric-loom, licensed under the MIT License (MIT). * - * Copyright (c) 2021-2024 FabricMC + * Copyright (c) 2021-2025 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 @@ -295,8 +295,8 @@ public abstract class LoomGradleExtensionImpl extends LoomGradleExtensionApiImpl provider.getDownloader().set(this::download); provider.getDownloader().disallowChanges(); - provider.getIsLegacyMinecraft().set(getProject().provider(() -> getMinecraftProvider().isLegacyVersion())); - provider.getIsLegacyMinecraft().disallowChanges(); + provider.getUseSplitOfficialNamespaces().set(getProject().provider(() -> getMinecraftProvider().isLegacySplitOfficialNamespaceVersion())); + provider.getUseSplitOfficialNamespaces().disallowChanges(); } @Override diff --git a/src/main/java/net/fabricmc/loom/util/Constants.java b/src/main/java/net/fabricmc/loom/util/Constants.java index c0abfce9..6f6e3236 100644 --- a/src/main/java/net/fabricmc/loom/util/Constants.java +++ b/src/main/java/net/fabricmc/loom/util/Constants.java @@ -1,7 +1,7 @@ /* * This file is part of fabric-loom, licensed under the MIT License (MIT). * - * Copyright (c) 2016-2022 FabricMC + * Copyright (c) 2016-2025 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 @@ -36,6 +36,7 @@ public class Constants { public static final int ASM_VERSION = Opcodes.ASM9; public static final String RELEASE_TIME_1_3 = "2012-07-25T22:00:00+00:00"; + public static final String RELEASE_TIME_BETA_1_0 = "2010-12-19T22:00:00+00:00"; private Constants() { } diff --git a/src/test/groovy/net/fabricmc/loom/test/integration/LegacyProjectTest.groovy b/src/test/groovy/net/fabricmc/loom/test/integration/LegacyProjectTest.groovy index 1cb8e760..c2a1680e 100644 --- a/src/test/groovy/net/fabricmc/loom/test/integration/LegacyProjectTest.groovy +++ b/src/test/groovy/net/fabricmc/loom/test/integration/LegacyProjectTest.groovy @@ -1,7 +1,7 @@ /* * This file is part of fabric-loom, licensed under the MIT License (MIT). * - * Copyright (c) 2018-2022 FabricMC + * Copyright (c) 2018-2025 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 @@ -24,6 +24,7 @@ package net.fabricmc.loom.test.integration +import java.nio.file.Files import java.nio.file.Path import spock.lang.Specification @@ -147,4 +148,40 @@ class LegacyProjectTest extends Specification implements GradleProjectTestTrait then: result.task(":build").outcome == SUCCESS } + + def "Legacy single jar version with mappings but no intermediates"() { + setup: + def mappings = Path.of('src/test/resources/mappings/0.30-minimal.tiny') + def gradle = gradleProject(project: "minimalBase", version: PRE_RELEASE_GRADLE) + + Files.copy(mappings, gradle.projectDir.toPath().resolve('mappings.tiny')) + gradle.buildGradle << """ + loom.noIntermediateMappings() + + dependencies { + minecraft "com.mojang:minecraft:c0.30_01c" + mappings loom.layered { + it.mappings file("mappings.tiny") + } + + modImplementation "net.fabricmc:fabric-loader:0.15.7" + } + """ + def sourceFile = new File(gradle.projectDir, 'src/main/java/Test.java') + sourceFile.parentFile.mkdirs() + sourceFile.text = """ + public final class Test { + public static void foo() { + // Reference a mapped class + System.out.println(com.mojang.minecraft.Minecraft.class); + } + } + """ + + when: + def result = gradle.run(task: "build") + + then: + result.task(":build").outcome == SUCCESS + } } diff --git a/src/test/groovy/net/fabricmc/loom/test/unit/MappingsMergerTest.groovy b/src/test/groovy/net/fabricmc/loom/test/unit/MappingsMergerTest.groovy index 8bf8db99..d37a97b0 100644 --- a/src/test/groovy/net/fabricmc/loom/test/unit/MappingsMergerTest.groovy +++ b/src/test/groovy/net/fabricmc/loom/test/unit/MappingsMergerTest.groovy @@ -1,7 +1,7 @@ /* * This file is part of fabric-loom, licensed under the MIT License (MIT). * - * Copyright (c) 2024 FabricMC + * Copyright (c) 2024-2025 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 @@ -36,8 +36,6 @@ import net.fabricmc.mappingio.MappingReader import net.fabricmc.mappingio.adapter.MappingSourceNsSwitch import net.fabricmc.mappingio.tree.MemoryMappingTree -import static org.junit.jupiter.api.Assertions.* - class MappingsMergerTest { @TempDir Path tempDir @@ -106,7 +104,7 @@ class MappingsMergerTest { IntermediateMappingsService intermediateMappingsService = LoomMocks.intermediateMappingsServiceMock(intermediateMappingsServiceOptions) when: - MappingsMerger.legacyMergeAndSaveMappings(mappingsTiny, mergedMappingsTiny, intermediateMappingsService) + MappingsMerger.legacyMergedMergeAndSaveMappings(mappingsTiny, mergedMappingsTiny, intermediateMappingsService) def mappings = new MemoryMappingTree() MappingReader.read(mergedMappingsTiny, mappings) diff --git a/src/test/resources/mappings/0.30-minimal.tiny b/src/test/resources/mappings/0.30-minimal.tiny new file mode 100644 index 00000000..ba4779c7 --- /dev/null +++ b/src/test/resources/mappings/0.30-minimal.tiny @@ -0,0 +1,2 @@ +tiny 2 0 official intermediary named +c com/mojang/minecraft/l com/mojang/minecraft/l com/mojang/minecraft/Minecraft