diff --git a/src/main/java/net/fabricmc/loom/configuration/providers/mappings/MappingConfiguration.java b/src/main/java/net/fabricmc/loom/configuration/providers/mappings/MappingConfiguration.java index 317d4d364..a32883daf 100644 --- a/src/main/java/net/fabricmc/loom/configuration/providers/mappings/MappingConfiguration.java +++ b/src/main/java/net/fabricmc/loom/configuration/providers/mappings/MappingConfiguration.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-2023 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 @@ -97,7 +97,7 @@ public static MappingConfiguration create(Project project, SharedServiceManager final TinyJarInfo jarInfo = TinyJarInfo.get(inputJar); jarInfo.minecraftVersionId().ifPresent(id -> { if (!minecraftProvider.minecraftVersion().equals(id)) { - LOGGER.warn("The mappings (%s) were not build for minecraft version (%s) produce with caution.".formatted(dependency.getDepString(), minecraftProvider.minecraftVersion())); + LOGGER.warn("The mappings (%s) were not built for Minecraft version %s, proceed with caution.".formatted(dependency.getDepString(), minecraftProvider.minecraftVersion())); } }); diff --git a/src/main/java/net/fabricmc/loom/configuration/providers/mappings/tiny/TinyJarInfo.java b/src/main/java/net/fabricmc/loom/configuration/providers/mappings/tiny/TinyJarInfo.java index 8ea1efc11..121b444a4 100644 --- a/src/main/java/net/fabricmc/loom/configuration/providers/mappings/tiny/TinyJarInfo.java +++ b/src/main/java/net/fabricmc/loom/configuration/providers/mappings/tiny/TinyJarInfo.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-2023 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 @@ -26,29 +26,49 @@ import java.io.BufferedReader; import java.io.IOException; +import java.io.InputStream; import java.io.UncheckedIOException; import java.nio.file.Files; import java.nio.file.Path; import java.util.Optional; +import java.util.jar.Manifest; import net.fabricmc.loom.util.FileSystemUtil; import net.fabricmc.mappingio.MappingReader; import net.fabricmc.mappingio.format.MappingFormat; public record TinyJarInfo(boolean v2, Optional minecraftVersionId) { + private static final String MANIFEST_PATH = "META-INF/MANIFEST.MF"; + private static final String MANIFEST_VERSION_ID_ATTRIBUTE = "Minecraft-Version-Id"; + public static TinyJarInfo get(Path jar) { - try { - return new TinyJarInfo(doesJarContainV2Mappings(jar), Optional.empty()); + try (FileSystemUtil.Delegate delegate = FileSystemUtil.getJarFileSystem(jar)) { + return new TinyJarInfo(doesJarContainV2Mappings(delegate), getMinecraftVersionId(delegate)); } catch (IOException e) { throw new UncheckedIOException("Failed to read tiny jar info", e); } } - private static boolean doesJarContainV2Mappings(Path path) throws IOException { - try (FileSystemUtil.Delegate delegate = FileSystemUtil.getJarFileSystem(path)) { - try (BufferedReader reader = Files.newBufferedReader(delegate.fs().getPath("mappings", "mappings.tiny"))) { - return MappingReader.detectFormat(reader) == MappingFormat.TINY_2_FILE; + private static boolean doesJarContainV2Mappings(FileSystemUtil.Delegate fs) throws IOException { + try (BufferedReader reader = Files.newBufferedReader(fs.getPath("mappings", "mappings.tiny"))) { + return MappingReader.detectFormat(reader) == MappingFormat.TINY_2_FILE; + } + } + + private static Optional getMinecraftVersionId(FileSystemUtil.Delegate fs) throws IOException { + final Path manifestPath = fs.getPath(MANIFEST_PATH); + + if (Files.exists(manifestPath)) { + final var manifest = new Manifest(); + + try (InputStream in = Files.newInputStream(manifestPath)) { + manifest.read(in); } + + final String minecraftVersionId = manifest.getMainAttributes().getValue(MANIFEST_VERSION_ID_ATTRIBUTE); + return Optional.ofNullable(minecraftVersionId); } + + return Optional.empty(); } } diff --git a/src/test/groovy/net/fabricmc/loom/test/unit/providers/TinyJarInfoTest.groovy b/src/test/groovy/net/fabricmc/loom/test/unit/providers/TinyJarInfoTest.groovy new file mode 100644 index 000000000..df3b3c3a7 --- /dev/null +++ b/src/test/groovy/net/fabricmc/loom/test/unit/providers/TinyJarInfoTest.groovy @@ -0,0 +1,97 @@ +/* + * This file is part of fabric-loom, licensed under the MIT License (MIT). + * + * Copyright (c) 2023 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.test.unit.providers + +import java.nio.file.Path +import java.util.jar.Attributes +import java.util.jar.Manifest + +import spock.lang.Specification +import spock.lang.TempDir + +import net.fabricmc.loom.configuration.providers.mappings.tiny.TinyJarInfo +import net.fabricmc.loom.util.ZipUtils + +class TinyJarInfoTest extends Specification { + @TempDir + Path tempDir + Path v1MappingsJar + Path v2MappingsJar + + def setup() { + v1MappingsJar = tempDir.resolve('mappings-v1.jar') + v2MappingsJar = tempDir.resolve('mappings-v2.jar') + ZipUtils.add(v1MappingsJar, 'mappings/mappings.tiny', 'v1\tintermediary\tnamed\n') + ZipUtils.add(v2MappingsJar, 'mappings/mappings.tiny', 'tiny\t2\t0\tintermediary\tnamed\n') + } + + def "v1 without minecraft version"() { + when: + def jarInfo = TinyJarInfo.get(v1MappingsJar) + + then: + jarInfo == new TinyJarInfo(false, Optional.empty()) + } + + def "v2 without minecraft version"() { + when: + def jarInfo = TinyJarInfo.get(v2MappingsJar) + + then: + jarInfo == new TinyJarInfo(true, Optional.empty()) + } + + def "v1 with minecraft version"() { + setup: + def manifest = new Manifest() + manifest.mainAttributes.put(Attributes.Name.MANIFEST_VERSION, '1.0') + manifest.mainAttributes.putValue('Minecraft-Version-Id', '18w50a') + def out = new ByteArrayOutputStream() + manifest.write(out) + ZipUtils.add(v1MappingsJar, 'META-INF/MANIFEST.MF', out.toByteArray()) + + when: + def jarInfo = TinyJarInfo.get(v1MappingsJar) + + then: + jarInfo == new TinyJarInfo(false, Optional.of('18w50a')) + } + + def "v2 with minecraft version"() { + setup: + def manifest = new Manifest() + manifest.mainAttributes.put(Attributes.Name.MANIFEST_VERSION, '1.0') + manifest.mainAttributes.putValue('Minecraft-Version-Id', '18w50a') + def out = new ByteArrayOutputStream() + manifest.write(out) + ZipUtils.add(v2MappingsJar, 'META-INF/MANIFEST.MF', out.toByteArray()) + + when: + def jarInfo = TinyJarInfo.get(v2MappingsJar) + + then: + jarInfo == new TinyJarInfo(true, Optional.of('18w50a')) + } +}