Support merging pre 1.3 Minecraft versions. (#1026)

Co-authored-by: modmuss50 <modmuss50@gmail.com>
This commit is contained in:
Space Walker
2024-03-19 18:25:57 +01:00
committed by GitHub
parent 30ef45e878
commit 3670ccb959
30 changed files with 490 additions and 39 deletions

View File

@@ -89,7 +89,7 @@ public interface LoomGradleExtension extends LoomGradleExtensionAPI {
return switch (mappingsNamespace) {
case NAMED -> getNamedMinecraftProvider().getMinecraftJarPaths();
case INTERMEDIARY -> getIntermediaryMinecraftProvider().getMinecraftJarPaths();
case OFFICIAL -> getMinecraftProvider().getMinecraftJars();
case OFFICIAL, CLIENT_OFFICIAL, SERVER_OFFICIAL -> getMinecraftProvider().getMinecraftJars();
};
}

View File

@@ -204,6 +204,7 @@ public interface LoomGradleExtensionAPI {
*/
Property<String> getIntermediaryUrl();
@ApiStatus.Experimental
Property<MinecraftJarConfiguration<?, ?, ?>> getMinecraftJarConfiguration();
default void serverOnlyMinecraftJar() {

View File

@@ -44,6 +44,14 @@ public abstract class IntermediateMappingsProvider implements Named {
public abstract Property<Function<String, DownloadBuilder>> getDownloader();
/**
* Set to true if the minecraft version is 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<Boolean> getIsLegacyMinecraft();
/**
* Generate or download a tinyv2 mapping file with intermediary and named namespaces.
* @throws IOException

View File

@@ -37,6 +37,18 @@ public enum MappingsNamespace {
*/
OFFICIAL,
/**
* Official names for the Minecraft client jar, usually obfuscated.
* This namespace is used for versions <1.3, where the client and server jars are obfuscated differently.
*/
CLIENT_OFFICIAL,
/**
* Official names for the Minecraft server jar, usually obfuscated.
* This namespace is used for versions <1.3, where the client and server jars are obfuscated differently.
*/
SERVER_OFFICIAL,
/**
* Intermediary mappings have been generated to provide a stable set of names across minecraft versions.
*
@@ -60,6 +72,8 @@ public enum MappingsNamespace {
public static @Nullable MappingsNamespace of(String namespace) {
return switch (namespace) {
case "official" -> OFFICIAL;
case "clientOfficial" -> CLIENT_OFFICIAL;
case "serverOfficial" -> SERVER_OFFICIAL;
case "intermediary" -> INTERMEDIARY;
case "named" -> NAMED;
default -> null;
@@ -68,6 +82,10 @@ public enum MappingsNamespace {
@Override
public String toString() {
return name().toLowerCase(Locale.ROOT);
return switch (this) {
case CLIENT_OFFICIAL -> "clientOfficial";
case SERVER_OFFICIAL -> "serverOfficial";
default -> name().toLowerCase(Locale.ROOT);
};
}
}

View File

@@ -60,6 +60,7 @@ import net.fabricmc.loom.configuration.processors.MinecraftJarProcessorManager;
import net.fabricmc.loom.configuration.processors.ModJavadocProcessor;
import net.fabricmc.loom.configuration.providers.mappings.LayeredMappingsFactory;
import net.fabricmc.loom.configuration.providers.mappings.MappingConfiguration;
import net.fabricmc.loom.configuration.providers.minecraft.MinecraftJarConfiguration;
import net.fabricmc.loom.configuration.providers.minecraft.MinecraftMetadataProvider;
import net.fabricmc.loom.configuration.providers.minecraft.MinecraftProvider;
import net.fabricmc.loom.configuration.providers.minecraft.MinecraftSourceSets;
@@ -68,6 +69,7 @@ import net.fabricmc.loom.configuration.providers.minecraft.mapped.IntermediaryMi
import net.fabricmc.loom.configuration.providers.minecraft.mapped.NamedMinecraftProvider;
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.ProcessUtil;
import net.fabricmc.loom.util.gradle.GradleUtils;
@@ -154,7 +156,11 @@ public abstract class CompileConfiguration implements Runnable {
final MinecraftMetadataProvider metadataProvider = MinecraftMetadataProvider.create(configContext);
final var jarConfiguration = extension.getMinecraftJarConfiguration().get();
var jarConfiguration = extension.getMinecraftJarConfiguration().get();
if (jarConfiguration == MinecraftJarConfiguration.MERGED && !metadataProvider.getVersionMeta().isVersionOrNewer(Constants.RELEASE_TIME_1_3)) {
jarConfiguration = MinecraftJarConfiguration.LEGACY_MERGED;
}
// Provide the vanilla mc jars
final MinecraftProvider minecraftProvider = jarConfiguration.createMinecraftProvider(metadataProvider, configContext);

View File

@@ -50,10 +50,12 @@ import net.fabricmc.mappingio.tree.MemoryMappingTree;
public final class IntermediateMappingsService implements SharedService {
private final Path intermediaryTiny;
private final String expectedSrcNs;
private final Supplier<MemoryMappingTree> memoryMappingTree = Suppliers.memoize(this::createMemoryMappingTree);
private IntermediateMappingsService(Path intermediaryTiny) {
private IntermediateMappingsService(Path intermediaryTiny, String expectedSrcNs) {
this.intermediaryTiny = intermediaryTiny;
this.expectedSrcNs = expectedSrcNs;
}
public static synchronized IntermediateMappingsService getInstance(SharedServiceManager sharedServiceManager, Project project, MinecraftProvider minecraftProvider) {
@@ -84,7 +86,13 @@ public final class IntermediateMappingsService implements SharedService {
throw new UncheckedIOException("Failed to provide intermediate mappings", e);
}
return new IntermediateMappingsService(intermediaryTiny);
// When merging legacy versions there will be multiple named namespaces, so use intermediary as the common src ns
// Newer versions will use intermediary as the src ns
final String expectedSrcNs = minecraftProvider.isLegacyVersion()
? MappingsNamespace.INTERMEDIARY.toString() // <1.3
: MappingsNamespace.OFFICIAL.toString(); // >=1.3
return new IntermediateMappingsService(intermediaryTiny, expectedSrcNs);
}
private MemoryMappingTree createMemoryMappingTree() {
@@ -100,6 +108,10 @@ public final class IntermediateMappingsService implements SharedService {
throw new UncheckedIOException("Failed to read intermediary mappings", e);
}
if (!expectedSrcNs.equals(tree.getSrcNamespace())) {
throw new RuntimeException("Invalid intermediate mappings: expected source namespace '" + expectedSrcNs + "' but found '" + tree.getSrcNamespace() + "\'");
}
return tree;
}

View File

@@ -176,7 +176,7 @@ public class MappingConfiguration {
// These are unmerged v2 mappings
IntermediateMappingsService intermediateMappingsService = IntermediateMappingsService.getInstance(serviceManager, project, minecraftProvider);
MappingsMerger.mergeAndSaveMappings(baseTinyMappings, tinyMappings, intermediateMappingsService);
MappingsMerger.mergeAndSaveMappings(baseTinyMappings, tinyMappings, minecraftProvider, intermediateMappingsService);
} else {
final List<Path> minecraftJars = minecraftProvider.getMinecraftJars();

View File

@@ -37,11 +37,12 @@ import net.fabricmc.loom.api.mappings.intermediate.IntermediateMappingsProvider;
* A bit of a hack, creates an empty intermediary mapping file to be used for mc versions without any intermediate mappings.
*/
public abstract class NoOpIntermediateMappingsProvider extends IntermediateMappingsProvider {
private static final String HEADER = "tiny\t2\t0\tofficial\tintermediary";
private static final String HEADER_OFFICIAL_MERGED = "tiny\t2\t0\tofficial\tintermediary";
private static final String HEADER_OFFICIAL_LEGACY_MERGED = "tiny\t2\t0\tintermediary\tclientOfficial\tserverOfficial\t";
@Override
public void provide(Path tinyMappings) throws IOException {
Files.writeString(tinyMappings, HEADER, StandardCharsets.UTF_8);
Files.writeString(tinyMappings, getIsLegacyMinecraft().get() ? HEADER_OFFICIAL_LEGACY_MERGED : HEADER_OFFICIAL_MERGED, StandardCharsets.UTF_8);
}
@Override

View File

@@ -39,6 +39,7 @@ import org.slf4j.LoggerFactory;
import net.fabricmc.loom.api.mappings.layered.MappingsNamespace;
import net.fabricmc.loom.configuration.providers.mappings.IntermediateMappingsService;
import net.fabricmc.loom.configuration.providers.minecraft.MinecraftProvider;
import net.fabricmc.mappingio.adapter.MappingNsCompleter;
import net.fabricmc.mappingio.adapter.MappingSourceNsSwitch;
import net.fabricmc.mappingio.format.tiny.Tiny2FileReader;
@@ -49,10 +50,20 @@ import net.fabricmc.mappingio.tree.MemoryMappingTree;
public final class MappingsMerger {
private static final Logger LOGGER = LoggerFactory.getLogger(MappingsMerger.class);
public static void mergeAndSaveMappings(Path from, Path out, IntermediateMappingsService intermediateMappingsService) throws IOException {
public static void mergeAndSaveMappings(Path from, Path out, MinecraftProvider minecraftProvider, IntermediateMappingsService intermediateMappingsService) throws IOException {
Stopwatch stopwatch = Stopwatch.createStarted();
LOGGER.info(":merging mappings");
if (minecraftProvider.isLegacyVersion()) {
legacyMergeAndSaveMappings(from, out, intermediateMappingsService);
} else {
mergeAndSaveMappings(from, out, intermediateMappingsService);
}
LOGGER.info(":merged mappings in " + stopwatch.stop());
}
private static void mergeAndSaveMappings(Path from, Path out, IntermediateMappingsService intermediateMappingsService) throws IOException {
MemoryMappingTree intermediaryTree = new MemoryMappingTree();
intermediateMappingsService.getMemoryMappingTree().accept(new MappingSourceNsSwitch(intermediaryTree, MappingsNamespace.INTERMEDIARY.toString()));
@@ -70,8 +81,27 @@ public final class MappingsMerger {
try (var writer = new Tiny2FileWriter(Files.newBufferedWriter(out, StandardCharsets.UTF_8), false)) {
officialTree.accept(writer);
}
}
LOGGER.info(":merged mappings in " + stopwatch.stop());
private static void legacyMergeAndSaveMappings(Path from, Path out, IntermediateMappingsService intermediateMappingsService) throws IOException {
MemoryMappingTree intermediaryTree = new MemoryMappingTree();
intermediateMappingsService.getMemoryMappingTree().accept(intermediaryTree);
try (BufferedReader reader = Files.newBufferedReader(from, StandardCharsets.UTF_8)) {
Tiny2FileReader.read(reader, intermediaryTree);
}
MemoryMappingTree officialTree = new MemoryMappingTree();
MappingNsCompleter nsCompleter = new MappingNsCompleter(officialTree, Map.of(MappingsNamespace.CLIENT_OFFICIAL.toString(), MappingsNamespace.INTERMEDIARY.toString(), MappingsNamespace.SERVER_OFFICIAL.toString(), MappingsNamespace.INTERMEDIARY.toString()));
intermediaryTree.accept(nsCompleter);
// versions this old strip inner class attributes
// from the obfuscated jars anyway
//inheritMappedNamesOfEnclosingClasses(officialTree);
try (var writer = new Tiny2FileWriter(Files.newBufferedWriter(out, StandardCharsets.UTF_8), false)) {
officialTree.accept(writer);
}
}
/**

View File

@@ -0,0 +1,83 @@
/*
* This file is part of fabric-loom, licensed under the MIT License (MIT).
*
* Copyright (c) 2024 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.minecraft;
import java.nio.file.Path;
import java.util.List;
import net.fabricmc.loom.api.mappings.layered.MappingsNamespace;
import net.fabricmc.loom.configuration.ConfigContext;
/**
* Minecraft versions prior to 1.3 obfuscate the server and client jars differently.
* The obfuscated jars must be provided separately, and can be merged after remapping.
*/
public final class LegacyMergedMinecraftProvider extends MinecraftProvider {
private final SingleJarMinecraftProvider.Server serverMinecraftProvider;
private final SingleJarMinecraftProvider.Client clientMinecraftProvider;
public LegacyMergedMinecraftProvider(MinecraftMetadataProvider metadataProvider, ConfigContext configContext) {
super(metadataProvider, configContext);
serverMinecraftProvider = SingleJarMinecraftProvider.server(metadataProvider, configContext);
clientMinecraftProvider = SingleJarMinecraftProvider.client(metadataProvider, configContext);
if (!isLegacyVersion()) {
throw new RuntimeException("something has gone wrong - legacy-merged jar configuration selected but Minecraft " + metadataProvider.getMinecraftVersion() + " allows merging the obfuscated jars - the merged jar configuration should have been selected!");
}
}
public SingleJarMinecraftProvider.Server getServerMinecraftProvider() {
return serverMinecraftProvider;
}
public SingleJarMinecraftProvider.Client getClientMinecraftProvider() {
return clientMinecraftProvider;
}
@Override
public void provide() throws Exception {
if (!serverMinecraftProvider.provideServer() || !clientMinecraftProvider.provideClient()) {
throw new UnsupportedOperationException("This version does not provide both the client and server jars - please select the client-only or server-only jar configuration!");
}
serverMinecraftProvider.provide();
clientMinecraftProvider.provide();
}
@Override
public List<Path> getMinecraftJars() {
return List.of(
serverMinecraftProvider.getMinecraftEnvOnlyJar(),
clientMinecraftProvider.getMinecraftEnvOnlyJar()
);
}
@Override
@Deprecated
public MappingsNamespace getOfficialNamespace() {
// Legacy merged providers do not have a single namespace as they delegate to the single jar providers
throw new UnsupportedOperationException("Cannot query the official namespace for legacy-merged minecraft providers");
}
}

View File

@@ -34,6 +34,7 @@ import java.util.Objects;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import net.fabricmc.loom.api.mappings.layered.MappingsNamespace;
import net.fabricmc.loom.configuration.ConfigContext;
public final class MergedMinecraftProvider extends MinecraftProvider {
@@ -43,6 +44,10 @@ public final class MergedMinecraftProvider extends MinecraftProvider {
public MergedMinecraftProvider(MinecraftMetadataProvider metadataProvider, ConfigContext configContext) {
super(metadataProvider, configContext);
if (isLegacyVersion()) {
throw new RuntimeException("something has gone wrong - merged jar configuration selected but Minecraft " + metadataProvider.getMinecraftVersion() + " does not allow merging the obfuscated jars - the legacy-merged jar configuration should have been selected!");
}
}
@Override
@@ -56,12 +61,17 @@ public final class MergedMinecraftProvider extends MinecraftProvider {
return List.of(minecraftMergedJar);
}
@Override
public MappingsNamespace getOfficialNamespace() {
return MappingsNamespace.OFFICIAL;
}
@Override
public void provide() throws Exception {
super.provide();
if (!getVersionInfo().isVersionOrNewer("2012-07-25T22:00:00+00:00" /* 1.3 release date */)) {
throw new UnsupportedOperationException("Minecraft versions 1.2.5 and older cannot be merged. Please use `loom { server/clientOnlyMinecraftJar() }`");
if (!provideServer() || !provideClient()) {
throw new UnsupportedOperationException("This version does not provide both the client and server jars - please select the client-only or server-only jar configuration!");
}
if (!Files.exists(minecraftMergedJar) || getExtension().refreshDeps()) {
@@ -79,18 +89,24 @@ public final class MergedMinecraftProvider extends MinecraftProvider {
}
private void mergeJars() throws IOException {
LOGGER.info(":merging jars");
File jarToMerge = getMinecraftServerJar();
File minecraftClientJar = getMinecraftClientJar();
File minecraftServerJar = getMinecraftServerJar();
if (getServerBundleMetadata() != null) {
extractBundledServerJar();
jarToMerge = getMinecraftExtractedServerJar();
minecraftServerJar = getMinecraftExtractedServerJar();
}
Objects.requireNonNull(jarToMerge, "Cannot merge null input jar?");
mergeJars(minecraftClientJar, minecraftServerJar, minecraftMergedJar.toFile());
}
try (var jarMerger = new MinecraftJarMerger(getMinecraftClientJar(), jarToMerge, minecraftMergedJar.toFile())) {
public static void mergeJars(File clientJar, File serverJar, File mergedJar) throws IOException {
LOGGER.info(":merging jars");
Objects.requireNonNull(clientJar, "Cannot merge null client jar?");
Objects.requireNonNull(serverJar, "Cannot merge null server jar?");
try (var jarMerger = new MinecraftJarMerger(clientJar, serverJar, mergedJar)) {
jarMerger.enableSyntheticParamsOffset();
jarMerger.merge();
}

View File

@@ -257,6 +257,8 @@ public class MinecraftClassMerger {
int j = 0;
while (i < first.size() || j < second.size()) {
int saved = i + j;
while (i < first.size() && j < second.size()
&& first.get(i).equals(second.get(j))) {
out.add(first.get(i));
@@ -273,6 +275,20 @@ public class MinecraftClassMerger {
out.add(second.get(j));
j++;
}
// if the order is scrambled, it's not possible to merge
// the lists while preserving the order from both sides
if (i + j == saved) {
for (; i < first.size(); i++) {
out.add(first.get(i));
}
for (; j < second.size(); j++) {
if (!first.contains(second.get(j))) {
out.add(second.get(j));
}
}
}
}
return out;

View File

@@ -60,6 +60,17 @@ public record MinecraftJarConfiguration<
SingleJarDecompileConfiguration::new,
List.of("client", "server")
);
public static final MinecraftJarConfiguration<
LegacyMergedMinecraftProvider,
NamedMinecraftProvider.LegacyMergedImpl,
MappedMinecraftProvider> LEGACY_MERGED = new MinecraftJarConfiguration<>(
LegacyMergedMinecraftProvider::new,
IntermediaryMinecraftProvider.LegacyMergedImpl::new,
NamedMinecraftProvider.LegacyMergedImpl::new,
ProcessedNamedMinecraftProvider.LegacyMergedImpl::new,
SingleJarDecompileConfiguration::new,
List.of("client", "server")
);
public static final MinecraftJarConfiguration<
SingleJarMinecraftProvider,
NamedMinecraftProvider.SingleJarImpl,

View File

@@ -82,6 +82,8 @@ public class MinecraftJarMerger implements AutoCloseable {
}
}
Files.createDirectories(output.toPath().getParent());
this.inputClient = (inputClientFs = FileSystemUtil.getJarFileSystem(inputClient, false)).get().getPath("/");
this.inputServer = (inputServerFs = FileSystemUtil.getJarFileSystem(inputServer, false)).get().getPath("/");
this.outputFs = FileSystemUtil.getJarFileSystem(output, true);

View File

@@ -38,8 +38,10 @@ import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import net.fabricmc.loom.LoomGradleExtension;
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.download.DownloadExecutor;
import net.fabricmc.loom.util.download.GradleDownloadProgressListener;
import net.fabricmc.loom.util.gradle.ProgressGroup;
@@ -185,6 +187,13 @@ public abstract class MinecraftProvider {
return Objects.requireNonNull(metadataProvider, "Metadata provider not setup").getVersionMeta();
}
/**
* @return true if the minecraft version is older than 1.3.
*/
public boolean isLegacyVersion() {
return !getVersionInfo().isVersionOrNewer(Constants.RELEASE_TIME_1_3);
}
@Nullable
public BundleMetadata getServerBundleMetadata() {
return serverBundleMetadata;
@@ -192,6 +201,8 @@ public abstract class MinecraftProvider {
public abstract List<Path> getMinecraftJars();
public abstract MappingsNamespace getOfficialNamespace();
protected Project getProject() {
return configContext.project();
}

View File

@@ -28,25 +28,38 @@ import java.nio.file.Files;
import java.nio.file.Path;
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.tinyremapper.NonClassCopyMode;
import net.fabricmc.tinyremapper.OutputConsumerPath;
import net.fabricmc.tinyremapper.TinyRemapper;
public abstract sealed class SingleJarMinecraftProvider extends MinecraftProvider permits SingleJarMinecraftProvider.Server, SingleJarMinecraftProvider.Client {
private final MappingsNamespace officialNamespace;
private Path minecraftEnvOnlyJar;
private SingleJarMinecraftProvider(MinecraftMetadataProvider metadataProvider, ConfigContext configContext) {
private SingleJarMinecraftProvider(MinecraftMetadataProvider metadataProvider, ConfigContext configContext, MappingsNamespace officialNamespace) {
super(metadataProvider, configContext);
this.officialNamespace = officialNamespace;
}
public static SingleJarMinecraftProvider.Server server(MinecraftMetadataProvider metadataProvider, ConfigContext configContext) {
return new SingleJarMinecraftProvider.Server(metadataProvider, configContext);
return new SingleJarMinecraftProvider.Server(metadataProvider, configContext, getOfficialNamespace(metadataProvider, true));
}
public static SingleJarMinecraftProvider.Client client(MinecraftMetadataProvider metadataProvider, ConfigContext configContext) {
return new SingleJarMinecraftProvider.Client(metadataProvider, configContext);
return new SingleJarMinecraftProvider.Client(metadataProvider, configContext, getOfficialNamespace(metadataProvider, false));
}
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)) {
return server ? MappingsNamespace.SERVER_OFFICIAL : MappingsNamespace.CLIENT_OFFICIAL;
}
return MappingsNamespace.OFFICIAL;
}
@Override
@@ -66,7 +79,7 @@ public abstract sealed class SingleJarMinecraftProvider extends MinecraftProvide
super.provide();
// Server only JARs are supported on any version, client only JARs are pretty much useless after 1.3.
if (provideClient() && getVersionInfo().isVersionOrNewer("2012-07-25T22:00:00+00:00" /* 1.3 release date */)) {
if (provideClient() && !isLegacyVersion()) {
getProject().getLogger().warn("Using `clientOnlyMinecraftJar()` is not recommended for Minecraft versions 1.3 or newer.");
}
@@ -105,13 +118,18 @@ public abstract sealed class SingleJarMinecraftProvider extends MinecraftProvide
return minecraftEnvOnlyJar;
}
@Override
public MappingsNamespace getOfficialNamespace() {
return officialNamespace;
}
abstract SingleJarEnvType type();
abstract Path getInputJar(SingleJarMinecraftProvider provider) throws Exception;
public static final class Server extends SingleJarMinecraftProvider {
private Server(MinecraftMetadataProvider metadataProvider, ConfigContext configContext) {
super(metadataProvider, configContext);
private Server(MinecraftMetadataProvider metadataProvider, ConfigContext configContext, MappingsNamespace officialNamespace) {
super(metadataProvider, configContext, officialNamespace);
}
@Override
@@ -143,8 +161,8 @@ public abstract sealed class SingleJarMinecraftProvider extends MinecraftProvide
}
public static final class Client extends SingleJarMinecraftProvider {
private Client(MinecraftMetadataProvider metadataProvider, ConfigContext configContext) {
super(metadataProvider, configContext);
private Client(MinecraftMetadataProvider metadataProvider, ConfigContext configContext, MappingsNamespace officialNamespace) {
super(metadataProvider, configContext, officialNamespace);
}
@Override

View File

@@ -28,6 +28,7 @@ import java.nio.file.Files;
import java.nio.file.Path;
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;
@@ -52,6 +53,11 @@ public final class SplitMinecraftProvider extends MinecraftProvider {
return List.of(minecraftClientOnlyJar, minecraftCommonJar);
}
@Override
public MappingsNamespace getOfficialNamespace() {
return MappingsNamespace.OFFICIAL;
}
@Override
public void provide() throws Exception {
super.provide();

View File

@@ -46,6 +46,7 @@ import net.fabricmc.loom.configuration.providers.mappings.MappingConfiguration;
import net.fabricmc.loom.configuration.providers.minecraft.MinecraftJar;
import net.fabricmc.loom.configuration.providers.minecraft.MinecraftProvider;
import net.fabricmc.loom.configuration.providers.minecraft.MinecraftSourceSets;
import net.fabricmc.loom.configuration.providers.minecraft.MinecraftVersionMeta;
import net.fabricmc.loom.configuration.providers.minecraft.SignatureFixerApplyVisitor;
import net.fabricmc.loom.extension.LoomFiles;
import net.fabricmc.loom.util.SidedClassVisitor;
@@ -190,7 +191,10 @@ public abstract class AbstractMappedMinecraftProvider<M extends MinecraftProvide
Files.deleteIfExists(remappedJars.outputJarPath());
final Map<String, String> remappedSignatures = SignatureFixerApplyVisitor.getRemappedSignatures(getTargetNamespace() == MappingsNamespace.INTERMEDIARY, mappingConfiguration, getProject(), configContext.serviceManager(), toM);
TinyRemapper remapper = TinyRemapperHelper.getTinyRemapper(getProject(), configContext.serviceManager(), fromM, toM, true, (builder) -> {
final MinecraftVersionMeta.JavaVersion javaVersion = minecraftProvider.getVersionInfo().javaVersion();
final boolean fixRecords = javaVersion != null && javaVersion.majorVersion() >= 16;
TinyRemapper remapper = TinyRemapperHelper.getTinyRemapper(getProject(), configContext.serviceManager(), fromM, toM, fixRecords, (builder) -> {
builder.extraPostApplyVisitor(new SignatureFixerApplyVisitor(remappedSignatures));
configureRemapper(remappedJars, builder);
});

View File

@@ -29,14 +29,16 @@ import java.util.List;
import org.gradle.api.Project;
import net.fabricmc.loom.api.mappings.layered.MappingsNamespace;
import net.fabricmc.loom.configuration.providers.minecraft.LegacyMergedMinecraftProvider;
import net.fabricmc.loom.configuration.providers.minecraft.MergedMinecraftProvider;
import net.fabricmc.loom.configuration.providers.minecraft.MinecraftJar;
import net.fabricmc.loom.configuration.providers.minecraft.MinecraftProvider;
import net.fabricmc.loom.configuration.providers.minecraft.SingleJarEnvType;
import net.fabricmc.loom.configuration.providers.minecraft.SingleJarMinecraftProvider;
import net.fabricmc.loom.configuration.providers.minecraft.SplitMinecraftProvider;
import net.fabricmc.tinyremapper.TinyRemapper;
public abstract sealed class IntermediaryMinecraftProvider<M extends MinecraftProvider> extends AbstractMappedMinecraftProvider<M> permits IntermediaryMinecraftProvider.MergedImpl, IntermediaryMinecraftProvider.SingleJarImpl, IntermediaryMinecraftProvider.SplitImpl {
public abstract sealed class IntermediaryMinecraftProvider<M extends MinecraftProvider> extends AbstractMappedMinecraftProvider<M> permits IntermediaryMinecraftProvider.MergedImpl, IntermediaryMinecraftProvider.LegacyMergedImpl, IntermediaryMinecraftProvider.SingleJarImpl, IntermediaryMinecraftProvider.SplitImpl {
public IntermediaryMinecraftProvider(Project project, M minecraftProvider) {
super(project, minecraftProvider);
}
@@ -59,11 +61,49 @@ public abstract sealed class IntermediaryMinecraftProvider<M extends MinecraftPr
@Override
public List<RemappedJars> getRemappedJars() {
return List.of(
new RemappedJars(minecraftProvider.getMergedJar(), getMergedJar(), MappingsNamespace.OFFICIAL)
new RemappedJars(minecraftProvider.getMergedJar(), getMergedJar(), minecraftProvider.getOfficialNamespace())
);
}
}
public static final class LegacyMergedImpl extends IntermediaryMinecraftProvider<LegacyMergedMinecraftProvider> implements Merged {
private final SingleJarImpl server;
private final SingleJarImpl client;
public LegacyMergedImpl(Project project, LegacyMergedMinecraftProvider minecraftProvider) {
super(project, minecraftProvider);
server = new SingleJarImpl(project, minecraftProvider.getServerMinecraftProvider(), SingleJarEnvType.SERVER);
client = new SingleJarImpl(project, minecraftProvider.getClientMinecraftProvider(), SingleJarEnvType.CLIENT);
}
@Override
public List<MinecraftJar> provide(ProvideContext context) throws Exception {
// Map the client and server jars separately
server.provide(context);
client.provide(context);
// then merge them
MergedMinecraftProvider.mergeJars(
client.getEnvOnlyJar().toFile(),
server.getEnvOnlyJar().toFile(),
getMergedJar().toFile()
);
return List.of(getMergedJar());
}
@Override
public List<RemappedJars> getRemappedJars() {
// The delegate providers will handle the remapping
throw new UnsupportedOperationException("LegacyMergedImpl does not support getRemappedJars");
}
@Override
public List<MinecraftJar.Type> getDependencyTypes() {
return List.of(MinecraftJar.Type.MERGED);
}
}
public static final class SplitImpl extends IntermediaryMinecraftProvider<SplitMinecraftProvider> implements Split {
public SplitImpl(Project project, SplitMinecraftProvider minecraftProvider) {
super(project, minecraftProvider);
@@ -72,8 +112,8 @@ public abstract sealed class IntermediaryMinecraftProvider<M extends MinecraftPr
@Override
public List<RemappedJars> getRemappedJars() {
return List.of(
new RemappedJars(minecraftProvider.getMinecraftCommonJar(), getCommonJar(), MappingsNamespace.OFFICIAL),
new RemappedJars(minecraftProvider.getMinecraftClientOnlyJar(), getClientOnlyJar(), MappingsNamespace.OFFICIAL, minecraftProvider.getMinecraftCommonJar())
new RemappedJars(minecraftProvider.getMinecraftCommonJar(), getCommonJar(), minecraftProvider.getOfficialNamespace()),
new RemappedJars(minecraftProvider.getMinecraftClientOnlyJar(), getClientOnlyJar(), minecraftProvider.getOfficialNamespace(), minecraftProvider.getMinecraftCommonJar())
);
}
@@ -102,7 +142,7 @@ public abstract sealed class IntermediaryMinecraftProvider<M extends MinecraftPr
@Override
public List<RemappedJars> getRemappedJars() {
return List.of(
new RemappedJars(minecraftProvider.getMinecraftEnvOnlyJar(), getEnvOnlyJar(), MappingsNamespace.OFFICIAL)
new RemappedJars(minecraftProvider.getMinecraftEnvOnlyJar(), getEnvOnlyJar(), minecraftProvider.getOfficialNamespace())
);
}

View File

@@ -29,9 +29,11 @@ import java.util.List;
import org.gradle.api.Project;
import net.fabricmc.loom.api.mappings.layered.MappingsNamespace;
import net.fabricmc.loom.configuration.providers.minecraft.LegacyMergedMinecraftProvider;
import net.fabricmc.loom.configuration.providers.minecraft.MergedMinecraftProvider;
import net.fabricmc.loom.configuration.providers.minecraft.MinecraftJar;
import net.fabricmc.loom.configuration.providers.minecraft.MinecraftProvider;
import net.fabricmc.loom.configuration.providers.minecraft.MinecraftSourceSets;
import net.fabricmc.loom.configuration.providers.minecraft.SingleJarEnvType;
import net.fabricmc.loom.configuration.providers.minecraft.SingleJarMinecraftProvider;
import net.fabricmc.loom.configuration.providers.minecraft.SplitMinecraftProvider;
@@ -60,7 +62,7 @@ public abstract class NamedMinecraftProvider<M extends MinecraftProvider> extend
@Override
public List<RemappedJars> getRemappedJars() {
return List.of(
new RemappedJars(minecraftProvider.getMergedJar(), getMergedJar(), MappingsNamespace.OFFICIAL)
new RemappedJars(minecraftProvider.getMergedJar(), getMergedJar(), minecraftProvider.getOfficialNamespace())
);
}
@@ -70,6 +72,55 @@ public abstract class NamedMinecraftProvider<M extends MinecraftProvider> extend
}
}
public static final class LegacyMergedImpl extends NamedMinecraftProvider<LegacyMergedMinecraftProvider> implements Merged {
private final SingleJarImpl server;
private final SingleJarImpl client;
public LegacyMergedImpl(Project project, LegacyMergedMinecraftProvider minecraftProvider) {
super(project, minecraftProvider);
server = new SingleJarImpl(project, minecraftProvider.getServerMinecraftProvider(), SingleJarEnvType.SERVER);
client = new SingleJarImpl(project, minecraftProvider.getClientMinecraftProvider(), SingleJarEnvType.CLIENT);
}
@Override
public List<MinecraftJar> provide(ProvideContext context) throws Exception {
final ProvideContext childContext = context.withApplyDependencies(false);
// Map the client and server jars separately
server.provide(childContext);
client.provide(childContext);
// then merge them
MergedMinecraftProvider.mergeJars(
client.getEnvOnlyJar().toFile(),
server.getEnvOnlyJar().toFile(),
getMergedJar().toFile()
);
getMavenHelper(MinecraftJar.Type.MERGED).savePom();
if (context.applyDependencies()) {
MinecraftSourceSets.get(getProject()).applyDependencies(
(configuration, type) -> getProject().getDependencies().add(configuration, getDependencyNotation(type)),
getDependencyTypes()
);
}
return List.of(getMergedJar());
}
@Override
public List<RemappedJars> getRemappedJars() {
// The delegate providers will handle the remapping
throw new UnsupportedOperationException("LegacyMergedImpl does not support getRemappedJars");
}
@Override
public List<MinecraftJar.Type> getDependencyTypes() {
return List.of(MinecraftJar.Type.MERGED);
}
}
public static final class SplitImpl extends NamedMinecraftProvider<SplitMinecraftProvider> implements Split {
public SplitImpl(Project project, SplitMinecraftProvider minecraftProvider) {
super(project, minecraftProvider);
@@ -78,8 +129,8 @@ public abstract class NamedMinecraftProvider<M extends MinecraftProvider> extend
@Override
public List<RemappedJars> getRemappedJars() {
return List.of(
new RemappedJars(minecraftProvider.getMinecraftCommonJar(), getCommonJar(), MappingsNamespace.OFFICIAL),
new RemappedJars(minecraftProvider.getMinecraftClientOnlyJar(), getClientOnlyJar(), MappingsNamespace.OFFICIAL, minecraftProvider.getMinecraftCommonJar())
new RemappedJars(minecraftProvider.getMinecraftCommonJar(), getCommonJar(), minecraftProvider.getOfficialNamespace()),
new RemappedJars(minecraftProvider.getMinecraftClientOnlyJar(), getClientOnlyJar(), minecraftProvider.getOfficialNamespace(), minecraftProvider.getMinecraftCommonJar())
);
}
@@ -113,7 +164,7 @@ public abstract class NamedMinecraftProvider<M extends MinecraftProvider> extend
@Override
public List<RemappedJars> getRemappedJars() {
return List.of(
new RemappedJars(minecraftProvider.getMinecraftEnvOnlyJar(), getEnvOnlyJar(), MappingsNamespace.OFFICIAL)
new RemappedJars(minecraftProvider.getMinecraftEnvOnlyJar(), getEnvOnlyJar(), minecraftProvider.getOfficialNamespace())
);
}

View File

@@ -37,6 +37,7 @@ import net.fabricmc.loom.configuration.ConfigContext;
import net.fabricmc.loom.configuration.mods.dependency.LocalMavenHelper;
import net.fabricmc.loom.configuration.processors.MinecraftJarProcessorManager;
import net.fabricmc.loom.configuration.processors.ProcessorContextImpl;
import net.fabricmc.loom.configuration.providers.minecraft.LegacyMergedMinecraftProvider;
import net.fabricmc.loom.configuration.providers.minecraft.MergedMinecraftProvider;
import net.fabricmc.loom.configuration.providers.minecraft.MinecraftJar;
import net.fabricmc.loom.configuration.providers.minecraft.MinecraftProvider;
@@ -177,6 +178,17 @@ public abstract class ProcessedNamedMinecraftProvider<M extends MinecraftProvide
}
}
public static final class LegacyMergedImpl extends ProcessedNamedMinecraftProvider<LegacyMergedMinecraftProvider, NamedMinecraftProvider.LegacyMergedImpl> implements Merged {
public LegacyMergedImpl(NamedMinecraftProvider.LegacyMergedImpl parentMinecraftProvider, MinecraftJarProcessorManager jarProcessorManager) {
super(parentMinecraftProvider, jarProcessorManager);
}
@Override
public MinecraftJar getMergedJar() {
return getProcessedJar(getParentMinecraftProvider().getMergedJar());
}
}
public static final class SplitImpl extends ProcessedNamedMinecraftProvider<SplitMinecraftProvider, NamedMinecraftProvider.SplitImpl> implements Split {
public SplitImpl(NamedMinecraftProvider.SplitImpl parentMinecraftProvide, MinecraftJarProcessorManager jarProcessorManager) {
super(parentMinecraftProvide, jarProcessorManager);

View File

@@ -47,6 +47,7 @@ import net.fabricmc.loom.configuration.accesswidener.AccessWidenerFile;
import net.fabricmc.loom.configuration.providers.mappings.IntermediaryMappingsProvider;
import net.fabricmc.loom.configuration.providers.mappings.LayeredMappingsFactory;
import net.fabricmc.loom.configuration.providers.mappings.MappingConfiguration;
import net.fabricmc.loom.configuration.providers.mappings.NoOpIntermediateMappingsProvider;
import net.fabricmc.loom.configuration.providers.minecraft.MinecraftProvider;
import net.fabricmc.loom.configuration.providers.minecraft.library.LibraryProcessorManager;
import net.fabricmc.loom.configuration.providers.minecraft.mapped.IntermediaryMinecraftProvider;
@@ -165,6 +166,11 @@ public class LoomGradleExtensionImpl extends LoomGradleExtensionApiImpl implemen
this.intermediaryMinecraftProvider = intermediaryMinecraftProvider;
}
@Override
public void noIntermediateMappings() {
setIntermediateMappingsProvider(NoOpIntermediateMappingsProvider.class, p -> { });
}
@Override
public FileCollection getMinecraftJarsCollection(MappingsNamespace mappingsNamespace) {
return getProject().files(
@@ -271,6 +277,9 @@ public class LoomGradleExtensionImpl extends LoomGradleExtensionApiImpl implemen
provider.getDownloader().set(this::download);
provider.getDownloader().disallowChanges();
provider.getIsLegacyMinecraft().set(getProject().provider(() -> getMinecraftProvider().isLegacyVersion()));
provider.getIsLegacyMinecraft().disallowChanges();
}
@Override

View File

@@ -34,6 +34,7 @@ public class Constants {
public static final String FABRIC_REPOSITORY = "https://maven.fabricmc.net/";
public static final int ASM_VERSION = Opcodes.ASM9;
public static final String RELEASE_TIME_1_3 = "2012-07-25T22:00:00+00:00";
private Constants() {
}

View File

@@ -69,7 +69,7 @@ public final class TinyRemapperHelper {
MemoryMappingTree mappingTree = extension.getMappingConfiguration().getMappingsService(serviceManager).getMappingTree();
if (fixRecords && !mappingTree.getSrcNamespace().equals(fromM)) {
throw new IllegalStateException("Mappings src namespace must match remap src namespace");
throw new IllegalStateException("Mappings src namespace must match remap src namespace, expected " + fromM + " but got " + mappingTree.getSrcNamespace());
}
int intermediaryNsId = mappingTree.getNamespaceId(MappingsNamespace.INTERMEDIARY.toString());

View File

@@ -24,6 +24,8 @@
package net.fabricmc.loom.test.integration
import java.nio.file.Path
import spock.lang.Specification
import spock.lang.Unroll
@@ -118,4 +120,31 @@ class LegacyProjectTest extends Specification implements GradleProjectTestTrait
'b1.8.1' | _
'a1.2.5' | _
}
@Unroll
def "Legacy merged"() {
setup:
def mappings = Path.of("src/test/resources/mappings/1.2.5-intermediary.tiny.zip").toAbsolutePath()
def gradle = gradleProject(project: "minimalBase", version: PRE_RELEASE_GRADLE)
gradle.buildGradle << """
dependencies {
minecraft "com.mojang:minecraft:1.2.5"
mappings loom.layered() {
// No names
}
modImplementation "net.fabricmc:fabric-loader:0.15.7"
}
"""
gradle.buildSrc("legacyMergedIntermediary")
when:
def result = gradle.run(task: "build", args: [
"-Ploom.test.legacyMergedIntermediary.mappingPath=${mappings}"
])
then:
result.task(":build").outcome == SUCCESS
}
}

View File

@@ -0,0 +1,63 @@
/*
* This file is part of fabric-loom, licensed under the MIT License (MIT).
*
* Copyright (c) 2022 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.integration.buildSrc.legacyMergedIntermediary
import java.nio.file.Files
import java.nio.file.Path
import java.nio.file.Paths
import org.gradle.api.Plugin
import org.gradle.api.Project
import org.gradle.api.provider.Property
import net.fabricmc.loom.api.LoomGradleExtensionAPI
import net.fabricmc.loom.api.mappings.intermediate.IntermediateMappingsProvider
import net.fabricmc.loom.util.ZipUtils
class TestPlugin implements Plugin<Project> {
@Override
void apply(Project project) {
LoomGradleExtensionAPI extension = project.getExtensions().getByName("loom")
extension.setIntermediateMappingsProvider(LegacyIntermediaryProvider.class) {
mappingPath.set(project.property("loom.test.legacyMergedIntermediary.mappingPath"))
}
}
abstract static class LegacyIntermediaryProvider extends IntermediateMappingsProvider {
final String name = "legacyMerged"
abstract Property<String> getMappingPath();
@Override
void provide(Path tinyMappings) throws IOException {
if (getMinecraftVersion().get() != "1.2.5") {
throw new IllegalStateException("This plugin only supports Minecraft 1.2.5")
}
byte[] data = ZipUtils.unpack(Paths.get(getMappingPath().get()), "1.2.5-intermediary.tiny")
Files.write(tinyMappings, data)
}
}
}

View File

@@ -30,6 +30,7 @@ class IntermediaryMappingLayerTest extends LayeredMappingsSpecification {
def "Read intermediary mappings" () {
setup:
intermediaryUrl = INTERMEDIARY_1_17_URL
mockMinecraftProvider.getVersionInfo() >> VERSION_META_1_17
when:
def mappings = getSingleMapping(new IntermediaryMappingsSpec())
def tiny = getTiny(mappings)

View File

@@ -34,13 +34,13 @@ interface LayeredMappingsTestConstants {
client_mappings: new MinecraftVersionMeta.Download(null, "227d16f520848747a59bef6f490ae19dc290a804", 6431705, "https://launcher.mojang.com/v1/objects/227d16f520848747a59bef6f490ae19dc290a804/client.txt"),
server_mappings: new MinecraftVersionMeta.Download(null, "84d80036e14bc5c7894a4fad9dd9f367d3000334", 4948536, "https://launcher.mojang.com/v1/objects/84d80036e14bc5c7894a4fad9dd9f367d3000334/server.txt")
]
public static final MinecraftVersionMeta VERSION_META_1_17 = new MinecraftVersionMeta(null, null, null, 0, DOWNLOADS_1_17, null, null, null, null, 0, null, null, null, null)
public static final MinecraftVersionMeta VERSION_META_1_17 = new MinecraftVersionMeta(null, null, null, 0, DOWNLOADS_1_17, null, null, null, null, 0, "2021-06-08T11:00:40+00:00", null, null, null)
public static final Map<String, MinecraftVersionMeta.Download> DOWNLOADS_1_16_5 = [
client_mappings: new MinecraftVersionMeta.Download(null, "e3dfb0001e1079a1af72ee21517330edf52e6192", 5746047, "https://launcher.mojang.com/v1/objects/e3dfb0001e1079a1af72ee21517330edf52e6192/client.txt"),
server_mappings: new MinecraftVersionMeta.Download(null, "81d5c793695d8cde63afddb40dde88e3a88132ac", 4400926, "https://launcher.mojang.com/v1/objects/81d5c793695d8cde63afddb40dde88e3a88132ac/server.txt")
]
public static final MinecraftVersionMeta VERSION_META_1_16_5 = new MinecraftVersionMeta(null, null, null, 0, DOWNLOADS_1_16_5, null, null, null, null, 0, null, null, null, null)
public static final MinecraftVersionMeta VERSION_META_1_16_5 = new MinecraftVersionMeta(null, null, null, 0, DOWNLOADS_1_16_5, null, null, null, null, 0, "2021-01-14T16:05:32+00:00", null, null, null)
public static final String PARCHMENT_NOTATION = "org.parchmentmc.data:parchment-1.16.5:20210608-SNAPSHOT@zip"
public static final String PARCHMENT_URL = "https://maven.parchmentmc.net/org/parchmentmc/data/parchment-1.16.5/20210608-SNAPSHOT/parchment-1.16.5-20210608-SNAPSHOT.zip"

View File

@@ -0,0 +1,2 @@
The file `1.2.5-intermediary.tiny` was taken from OrnitheMC's "[Calamus](https://github.com/OrnitheMC/calamus/blob/gen2/mappings/1.2.5.tiny)" intermediaries under the CC0 license.
The file was rewritten in Tiny V2 format, but the mappings are otherwise unmodified.