Fix record signatures based on data provided by mappings. (#507)
Reads "extras/record_signatures.json" from mappings jar, just works with yarn.
Mojmap:
```
mappings loom.layered {
   officialMojangMappings()
   signatureFix("net.fabricmc🧶21w38a+build.9:v2")
}
```
			
			
This commit is contained in:
		
							parent
							
								
									7d2dad6e46
								
							
						
					
					
						commit
						676a498e0c
					
				
					 17 changed files with 347 additions and 46 deletions
				
			
		|  | @ -47,4 +47,10 @@ public interface LayeredMappingSpecBuilder { | |||
| 	} | ||||
| 
 | ||||
| 	LayeredMappingSpecBuilder parchment(Object object, Action<ParchmentMappingsSpecBuilder> action); | ||||
| 
 | ||||
| 	/** | ||||
| 	 * Add a signatureFix layer. Reads the @extras/record_signatures.json" file in a jar file such as yarn. | ||||
| 	 */ | ||||
| 	@ApiStatus.Experimental | ||||
| 	LayeredMappingSpecBuilder signatureFix(Object object); | ||||
| } | ||||
|  |  | |||
|  | @ -34,6 +34,7 @@ import net.fabricmc.loom.api.mappings.layered.spec.FileSpec; | |||
| import net.fabricmc.loom.api.mappings.layered.spec.LayeredMappingSpecBuilder; | ||||
| import net.fabricmc.loom.api.mappings.layered.spec.MappingsSpec; | ||||
| import net.fabricmc.loom.api.mappings.layered.spec.ParchmentMappingsSpecBuilder; | ||||
| import net.fabricmc.loom.configuration.providers.mappings.extras.signatures.SignatureFixesSpec; | ||||
| import net.fabricmc.loom.configuration.providers.mappings.intermediary.IntermediaryMappingsSpec; | ||||
| import net.fabricmc.loom.configuration.providers.mappings.mojmap.MojangMappingsSpec; | ||||
| import net.fabricmc.loom.configuration.providers.mappings.parchment.ParchmentMappingsSpecBuilderImpl; | ||||
|  | @ -59,6 +60,11 @@ public class LayeredMappingSpecBuilderImpl implements LayeredMappingSpecBuilder | |||
| 		return addLayer(builder.build()); | ||||
| 	} | ||||
| 
 | ||||
| 	@Override | ||||
| 	public LayeredMappingSpecBuilder signatureFix(Object object) { | ||||
| 		return addLayer(new SignatureFixesSpec(FileSpec.create(object))); | ||||
| 	} | ||||
| 
 | ||||
| 	public LayeredMappingSpec build() { | ||||
| 		List<MappingsSpec<?>> builtLayers = new LinkedList<>(); | ||||
| 		// Intermediary is always the base layer | ||||
|  |  | |||
|  | @ -32,6 +32,8 @@ import java.nio.charset.StandardCharsets; | |||
| import java.nio.file.Files; | ||||
| import java.nio.file.Path; | ||||
| import java.util.Collections; | ||||
| import java.util.List; | ||||
| import java.util.Map; | ||||
| import java.util.Objects; | ||||
| import java.util.Set; | ||||
| 
 | ||||
|  | @ -44,6 +46,7 @@ import org.zeroturnaround.zip.ZipUtil; | |||
| 
 | ||||
| import net.fabricmc.loom.LoomGradlePlugin; | ||||
| import net.fabricmc.loom.api.mappings.layered.MappingContext; | ||||
| import net.fabricmc.loom.api.mappings.layered.MappingLayer; | ||||
| import net.fabricmc.loom.api.mappings.layered.MappingsNamespace; | ||||
| import net.fabricmc.mappingio.adapter.MappingDstNsReorder; | ||||
| import net.fabricmc.mappingio.adapter.MappingSourceNsSwitch; | ||||
|  | @ -72,29 +75,51 @@ public class LayeredMappingsDependency implements SelfResolvingDependency { | |||
| 		if (!Files.exists(mappingsFile) || LoomGradlePlugin.refreshDeps) { | ||||
| 			try { | ||||
| 				var processor = new LayeredMappingsProcessor(layeredMappingSpec); | ||||
| 				MemoryMappingTree mappings = processor.getMappings(mappingContext); | ||||
| 				List<MappingLayer> layers = processor.resolveLayers(mappingContext); | ||||
| 
 | ||||
| 				try (Writer writer = new StringWriter()) { | ||||
| 					Tiny2Writer tiny2Writer = new Tiny2Writer(writer, false); | ||||
| 				Files.deleteIfExists(mappingsFile); | ||||
| 
 | ||||
| 					MappingDstNsReorder nsReorder = new MappingDstNsReorder(tiny2Writer, Collections.singletonList(MappingsNamespace.NAMED.toString())); | ||||
| 					MappingSourceNsSwitch nsSwitch = new MappingSourceNsSwitch(nsReorder, MappingsNamespace.INTERMEDIARY.toString(), true); | ||||
| 					mappings.accept(nsSwitch); | ||||
| 
 | ||||
| 					Files.deleteIfExists(mappingsFile); | ||||
| 
 | ||||
| 					ZipUtil.pack(new ZipEntrySource[] { | ||||
| 							new ByteSource("mappings/mappings.tiny", writer.toString().getBytes(StandardCharsets.UTF_8)) | ||||
| 					}, mappingsFile.toFile()); | ||||
| 				} | ||||
| 				writeMapping(processor, layers, mappingsFile); | ||||
| 				writeSignatureFixes(processor, layers, mappingsFile); | ||||
| 			} catch (IOException e) { | ||||
| 				throw new RuntimeException("Failed to resolve Mojang mappings", e); | ||||
| 				throw new RuntimeException("Failed to resolve layered mappings", e); | ||||
| 			} | ||||
| 		} | ||||
| 
 | ||||
| 		return Collections.singleton(mappingsFile.toFile()); | ||||
| 	} | ||||
| 
 | ||||
| 	private void writeMapping(LayeredMappingsProcessor processor, List<MappingLayer> layers, Path mappingsFile) throws IOException { | ||||
| 		MemoryMappingTree mappings = processor.getMappings(layers); | ||||
| 
 | ||||
| 		try (Writer writer = new StringWriter()) { | ||||
| 			Tiny2Writer tiny2Writer = new Tiny2Writer(writer, false); | ||||
| 
 | ||||
| 			MappingDstNsReorder nsReorder = new MappingDstNsReorder(tiny2Writer, Collections.singletonList(MappingsNamespace.NAMED.toString())); | ||||
| 			MappingSourceNsSwitch nsSwitch = new MappingSourceNsSwitch(nsReorder, MappingsNamespace.INTERMEDIARY.toString(), true); | ||||
| 			mappings.accept(nsSwitch); | ||||
| 
 | ||||
| 			ZipUtil.pack(new ZipEntrySource[] { | ||||
| 					new ByteSource("mappings/mappings.tiny", writer.toString().getBytes(StandardCharsets.UTF_8)) | ||||
| 			}, mappingsFile.toFile()); | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	private void writeSignatureFixes(LayeredMappingsProcessor processor, List<MappingLayer> layers, Path mappingsFile) throws IOException { | ||||
| 		Map<String, String> signatureFixes = processor.getSignatureFixes(layers); | ||||
| 
 | ||||
| 		if (signatureFixes == null) { | ||||
| 			return; | ||||
| 		} | ||||
| 
 | ||||
| 		byte[] data = LoomGradlePlugin.OBJECT_MAPPER.writeValueAsString(signatureFixes).getBytes(StandardCharsets.UTF_8); | ||||
| 
 | ||||
| 		ZipUtil.addEntry( | ||||
| 				mappingsFile.toFile(), | ||||
| 				new ByteSource("extras/record_signatures.json", data) | ||||
| 		); | ||||
| 	} | ||||
| 
 | ||||
| 	@Override | ||||
| 	public Set<File> resolve(boolean transitive) { | ||||
| 		return resolve(); | ||||
|  |  | |||
|  | @ -26,12 +26,19 @@ package net.fabricmc.loom.configuration.providers.mappings; | |||
| 
 | ||||
| import java.io.IOException; | ||||
| import java.util.ArrayList; | ||||
| import java.util.Collections; | ||||
| import java.util.HashMap; | ||||
| import java.util.LinkedList; | ||||
| import java.util.List; | ||||
| import java.util.Map; | ||||
| 
 | ||||
| import org.jetbrains.annotations.Nullable; | ||||
| 
 | ||||
| import net.fabricmc.loom.api.mappings.layered.MappingContext; | ||||
| import net.fabricmc.loom.api.mappings.layered.MappingLayer; | ||||
| import net.fabricmc.loom.api.mappings.layered.MappingsNamespace; | ||||
| import net.fabricmc.loom.api.mappings.layered.spec.MappingsSpec; | ||||
| import net.fabricmc.loom.configuration.providers.mappings.extras.signatures.SignatureFixesLayer; | ||||
| import net.fabricmc.mappingio.adapter.MappingSourceNsSwitch; | ||||
| import net.fabricmc.mappingio.tree.MemoryMappingTree; | ||||
| 
 | ||||
|  | @ -42,9 +49,8 @@ public class LayeredMappingsProcessor { | |||
| 		this.layeredMappingSpec = spec; | ||||
| 	} | ||||
| 
 | ||||
| 	public MemoryMappingTree getMappings(MappingContext context) throws IOException { | ||||
| 		MemoryMappingTree mappingTree = new MemoryMappingTree(); | ||||
| 
 | ||||
| 	public List<MappingLayer> resolveLayers(MappingContext context) { | ||||
| 		List<MappingLayer> layers = new LinkedList<>(); | ||||
| 		List<Class<? extends MappingLayer>> visitedLayers = new ArrayList<>(); | ||||
| 
 | ||||
| 		for (MappingsSpec<?> spec : layeredMappingSpec.layers()) { | ||||
|  | @ -56,8 +62,17 @@ public class LayeredMappingsProcessor { | |||
| 				} | ||||
| 			} | ||||
| 
 | ||||
| 			layers.add(layer); | ||||
| 			visitedLayers.add(layer.getClass()); | ||||
| 		} | ||||
| 
 | ||||
| 		return Collections.unmodifiableList(layers); | ||||
| 	} | ||||
| 
 | ||||
| 	public MemoryMappingTree getMappings(List<MappingLayer> layers) throws IOException { | ||||
| 		MemoryMappingTree mappingTree = new MemoryMappingTree(); | ||||
| 
 | ||||
| 		for (MappingLayer layer : layers) { | ||||
| 			// We have to rebuild a new tree to work on when a layer doesnt merge into layered | ||||
| 			boolean rebuild = layer.getSourceNamespace() != MappingsNamespace.NAMED; | ||||
| 			MemoryMappingTree workingTree; | ||||
|  | @ -90,4 +105,21 @@ public class LayeredMappingsProcessor { | |||
| 
 | ||||
| 		return mappingTree; | ||||
| 	} | ||||
| 
 | ||||
| 	@Nullable | ||||
| 	public Map<String, String> getSignatureFixes(List<MappingLayer> layers) { | ||||
| 		Map<String, String> signatureFixes = new HashMap<>(); | ||||
| 
 | ||||
| 		for (MappingLayer layer : layers) { | ||||
| 			if (layer instanceof SignatureFixesLayer signatureFixesLayer) { | ||||
| 				signatureFixes.putAll(signatureFixesLayer.getSignatureFixes()); | ||||
| 			} | ||||
| 		} | ||||
| 
 | ||||
| 		if (signatureFixes.isEmpty()) { | ||||
| 			return null; | ||||
| 		} | ||||
| 
 | ||||
| 		return Collections.unmodifiableMap(signatureFixes); | ||||
| 	} | ||||
| } | ||||
|  |  | |||
|  | @ -27,6 +27,7 @@ package net.fabricmc.loom.configuration.providers.mappings; | |||
| import java.io.BufferedReader; | ||||
| import java.io.File; | ||||
| import java.io.IOException; | ||||
| import java.io.Reader; | ||||
| import java.net.URL; | ||||
| import java.nio.charset.StandardCharsets; | ||||
| import java.nio.file.FileSystem; | ||||
|  | @ -37,6 +38,7 @@ import java.nio.file.StandardCopyOption; | |||
| import java.util.Arrays; | ||||
| import java.util.Collections; | ||||
| import java.util.Objects; | ||||
| import java.util.Map; | ||||
| import java.util.function.Consumer; | ||||
| import java.util.regex.Pattern; | ||||
| 
 | ||||
|  | @ -45,6 +47,7 @@ import com.google.common.net.UrlEscapers; | |||
| import com.google.gson.JsonObject; | ||||
| import org.apache.tools.ant.util.StringUtils; | ||||
| import org.gradle.api.Project; | ||||
| import org.jetbrains.annotations.Nullable; | ||||
| import org.zeroturnaround.zip.FileSource; | ||||
| import org.zeroturnaround.zip.ZipEntrySource; | ||||
| import org.zeroturnaround.zip.ZipUtil; | ||||
|  | @ -92,6 +95,7 @@ public class MappingsProviderImpl extends DependencyProvider implements Mappings | |||
| 	private boolean hasUnpickDefinitions; | ||||
| 	private UnpickMetadata unpickMetadata; | ||||
| 	private MemoryMappingTree mappingTree; | ||||
| 	private Map<String, String> signatureFixes; | ||||
| 
 | ||||
| 	public MappingsProviderImpl(Project project) { | ||||
| 		super(project); | ||||
|  | @ -120,7 +124,7 @@ public class MappingsProviderImpl extends DependencyProvider implements Mappings | |||
| 			storeMappings(getProject(), minecraftProvider, mappingsJar.toPath()); | ||||
| 		} else { | ||||
| 			try (FileSystem fileSystem = FileSystems.newFileSystem(mappingsJar.toPath(), (ClassLoader) null)) { | ||||
| 				extractUnpickDefinitions(fileSystem, unpickDefinitions); | ||||
| 				extractExtras(fileSystem); | ||||
| 			} | ||||
| 		} | ||||
| 
 | ||||
|  | @ -209,7 +213,7 @@ public class MappingsProviderImpl extends DependencyProvider implements Mappings | |||
| 
 | ||||
| 		try (FileSystem fileSystem = FileSystems.newFileSystem(yarnJar, (ClassLoader) null)) { | ||||
| 			extractMappings(fileSystem, baseTinyMappings); | ||||
| 			extractUnpickDefinitions(fileSystem, unpickDefinitions); | ||||
| 			extractExtras(fileSystem); | ||||
| 		} | ||||
| 
 | ||||
| 		if (areMappingsV2(baseTinyMappings)) { | ||||
|  | @ -253,7 +257,12 @@ public class MappingsProviderImpl extends DependencyProvider implements Mappings | |||
| 		Files.copy(jar.getPath("mappings/mappings.tiny"), extractTo, StandardCopyOption.REPLACE_EXISTING); | ||||
| 	} | ||||
| 
 | ||||
| 	private void extractUnpickDefinitions(FileSystem jar, Path extractTo) throws IOException { | ||||
| 	private void extractExtras(FileSystem jar) throws IOException { | ||||
| 		extractUnpickDefinitions(jar); | ||||
| 		extractSignatureFixes(jar); | ||||
| 	} | ||||
| 
 | ||||
| 	private void extractUnpickDefinitions(FileSystem jar) throws IOException { | ||||
| 		Path unpickPath = jar.getPath("extras/definitions.unpick"); | ||||
| 		Path unpickMetadataPath = jar.getPath("extras/unpick.json"); | ||||
| 
 | ||||
|  | @ -261,12 +270,25 @@ public class MappingsProviderImpl extends DependencyProvider implements Mappings | |||
| 			return; | ||||
| 		} | ||||
| 
 | ||||
| 		Files.copy(unpickPath, extractTo, StandardCopyOption.REPLACE_EXISTING); | ||||
| 		Files.copy(unpickPath, unpickDefinitions, StandardCopyOption.REPLACE_EXISTING); | ||||
| 
 | ||||
| 		unpickMetadata = parseUnpickMetadata(unpickMetadataPath); | ||||
| 		hasUnpickDefinitions = true; | ||||
| 	} | ||||
| 
 | ||||
| 	private void extractSignatureFixes(FileSystem jar) throws IOException { | ||||
| 		Path recordSignaturesJsonPath = jar.getPath("extras/record_signatures.json"); | ||||
| 
 | ||||
| 		if (!Files.exists(recordSignaturesJsonPath)) { | ||||
| 			return; | ||||
| 		} | ||||
| 
 | ||||
| 		try (Reader reader = Files.newBufferedReader(recordSignaturesJsonPath, StandardCharsets.UTF_8)) { | ||||
| 			//noinspection unchecked | ||||
| 			signatureFixes = LoomGradlePlugin.OBJECT_MAPPER.readValue(reader, Map.class); | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	private UnpickMetadata parseUnpickMetadata(Path input) throws IOException { | ||||
| 		JsonObject jsonObject = LoomGradlePlugin.GSON.fromJson(Files.readString(input), JsonObject.class); | ||||
| 
 | ||||
|  | @ -461,6 +483,11 @@ public class MappingsProviderImpl extends DependencyProvider implements Mappings | |||
| 		return hasUnpickDefinitions; | ||||
| 	} | ||||
| 
 | ||||
| 	@Nullable | ||||
| 	public Map<String, String> getSignatureFixes() { | ||||
| 		return signatureFixes; | ||||
| 	} | ||||
| 
 | ||||
| 	@Override | ||||
| 	public File intermediaryTinyFile() { | ||||
| 		try { | ||||
|  |  | |||
|  | @ -0,0 +1,34 @@ | |||
| /* | ||||
|  * 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.mappings.extras.signatures; | ||||
| 
 | ||||
| import java.util.Map; | ||||
| 
 | ||||
| import org.jetbrains.annotations.ApiStatus; | ||||
| 
 | ||||
| @ApiStatus.Experimental | ||||
| public interface SignatureFixesLayer { | ||||
| 	Map<String, String> getSignatureFixes(); | ||||
| } | ||||
|  | @ -0,0 +1,64 @@ | |||
| /* | ||||
|  * 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.mappings.extras.signatures; | ||||
| 
 | ||||
| import java.io.IOException; | ||||
| import java.io.InputStreamReader; | ||||
| import java.nio.file.Path; | ||||
| import java.util.Map; | ||||
| import java.util.Objects; | ||||
| import java.util.zip.ZipEntry; | ||||
| import java.util.zip.ZipFile; | ||||
| 
 | ||||
| import org.jetbrains.annotations.ApiStatus; | ||||
| 
 | ||||
| import net.fabricmc.loom.LoomGradlePlugin; | ||||
| import net.fabricmc.loom.api.mappings.layered.MappingLayer; | ||||
| import net.fabricmc.mappingio.MappingVisitor; | ||||
| 
 | ||||
| @ApiStatus.Experimental | ||||
| public record SignatureFixesLayerImpl(Path mappingsFile) implements MappingLayer, SignatureFixesLayer { | ||||
| 	private static final String SIGNATURE_FIXES_PATH = "extras/record_signatures.json"; | ||||
| 
 | ||||
| 	@Override | ||||
| 	public void visit(MappingVisitor mappingVisitor) throws IOException { | ||||
| 		// Nothing to do here | ||||
| 	} | ||||
| 
 | ||||
| 	@Override | ||||
| 	public Map<String, String> getSignatureFixes() { | ||||
| 		try (var zipFile = new ZipFile(mappingsFile().toFile())) { | ||||
| 			ZipEntry zipFileEntry = zipFile.getEntry(SIGNATURE_FIXES_PATH); | ||||
| 			Objects.requireNonNull(zipFileEntry, "Could not find %s in file".formatted(SIGNATURE_FIXES_PATH)); | ||||
| 
 | ||||
| 			try (var reader = new InputStreamReader(zipFile.getInputStream(zipFileEntry))) { | ||||
| 				//noinspection unchecked | ||||
| 				return LoomGradlePlugin.OBJECT_MAPPER.readValue(reader, Map.class); | ||||
| 			} | ||||
| 		} catch (IOException e) { | ||||
| 			throw new RuntimeException("Failed to extract signature fixes", e); | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
|  | @ -0,0 +1,39 @@ | |||
| /* | ||||
|  * 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.mappings.extras.signatures; | ||||
| 
 | ||||
| import org.jetbrains.annotations.ApiStatus; | ||||
| 
 | ||||
| import net.fabricmc.loom.api.mappings.layered.MappingContext; | ||||
| import net.fabricmc.loom.api.mappings.layered.spec.FileSpec; | ||||
| import net.fabricmc.loom.api.mappings.layered.spec.MappingsSpec; | ||||
| 
 | ||||
| @ApiStatus.Experimental | ||||
| public record SignatureFixesSpec(FileSpec fileSpec) implements MappingsSpec<SignatureFixesLayerImpl> { | ||||
| 	@Override | ||||
| 	public SignatureFixesLayerImpl createLayer(MappingContext context) { | ||||
| 		return new SignatureFixesLayerImpl(fileSpec.get(context)); | ||||
| 	} | ||||
| } | ||||
|  | @ -29,9 +29,15 @@ import java.io.IOException; | |||
| import java.nio.file.Files; | ||||
| import java.nio.file.Path; | ||||
| import java.util.Arrays; | ||||
| import java.util.HashMap; | ||||
| import java.util.Map; | ||||
| import java.util.Objects; | ||||
| import java.util.concurrent.atomic.AtomicReference; | ||||
| import java.util.function.Consumer; | ||||
| 
 | ||||
| import org.gradle.api.Project; | ||||
| import org.objectweb.asm.ClassVisitor; | ||||
| import org.objectweb.asm.commons.Remapper; | ||||
| 
 | ||||
| import net.fabricmc.loom.api.mappings.layered.MappingsNamespace; | ||||
| import net.fabricmc.loom.configuration.DependencyProvider; | ||||
|  | @ -41,6 +47,7 @@ import net.fabricmc.loom.util.Constants; | |||
| import net.fabricmc.loom.util.TinyRemapperHelper; | ||||
| import net.fabricmc.tinyremapper.OutputConsumerPath; | ||||
| import net.fabricmc.tinyremapper.TinyRemapper; | ||||
| import net.fabricmc.tinyremapper.api.TrClass; | ||||
| 
 | ||||
| public class MinecraftMappedProvider extends DependencyProvider { | ||||
| 	private File minecraftMappedJar; | ||||
|  | @ -101,13 +108,67 @@ public class MinecraftMappedProvider extends DependencyProvider { | |||
| 		Path outputIntermediary = minecraftIntermediaryJar.toPath(); | ||||
| 
 | ||||
| 		for (String toM : Arrays.asList(MappingsNamespace.NAMED.toString(), MappingsNamespace.INTERMEDIARY.toString())) { | ||||
| 			Path output = MappingsNamespace.NAMED.toString().equals(toM) ? outputMapped : outputIntermediary; | ||||
| 			final boolean toNamed = MappingsNamespace.NAMED.toString().equals(toM); | ||||
| 			final boolean toIntermediary = MappingsNamespace.INTERMEDIARY.toString().equals(toM); | ||||
| 			final boolean fixSignatures = mappingsProvider.getSignatureFixes() != null; | ||||
| 			final Path output = toNamed ? outputMapped : outputIntermediary; | ||||
| 
 | ||||
| 			getProject().getLogger().lifecycle(":remapping minecraft (TinyRemapper, " + fromM + " -> " + toM + ")"); | ||||
| 
 | ||||
| 			Files.deleteIfExists(output); | ||||
| 
 | ||||
| 			TinyRemapper remapper = TinyRemapperHelper.getTinyRemapper(getProject(), fromM, toM, true); | ||||
| 			// Bit ugly but whatever, the whole issue is a bit ugly :) | ||||
| 			AtomicReference<Map<String, String>> remappedSignatures = new AtomicReference<>(); | ||||
| 			TinyRemapper remapper = TinyRemapperHelper.getTinyRemapper(getProject(), fromM, toM, true, (builder) -> { | ||||
| 				if (!fixSignatures) { | ||||
| 					return; | ||||
| 				} | ||||
| 
 | ||||
| 				builder.extraPostApplyVisitor(new TinyRemapper.ApplyVisitorProvider() { | ||||
| 					@Override | ||||
| 					public ClassVisitor insertApplyVisitor(TrClass cls, ClassVisitor next) { | ||||
| 						return new ClassVisitor(Constants.ASM_VERSION, next) { | ||||
| 							@Override | ||||
| 							public void visit(int version, int access, String name, String signature, String superName, String[] interfaces) { | ||||
| 								Map<String, String> signatureFixes = Objects.requireNonNull(remappedSignatures.get(), "Could not get remapped signatures"); | ||||
| 
 | ||||
| 								if (signature == null) { | ||||
| 									signature = signatureFixes.getOrDefault(name, null); | ||||
| 
 | ||||
| 									if (signature != null) { | ||||
| 										getProject().getLogger().info("Replaced signature for {} with {}", name, signature); | ||||
| 									} | ||||
| 								} | ||||
| 
 | ||||
| 								super.visit(version, access, name, signature, superName, interfaces); | ||||
| 							} | ||||
| 						}; | ||||
| 					} | ||||
| 				}); | ||||
| 			}); | ||||
| 
 | ||||
| 			if (fixSignatures) { | ||||
| 				if (toIntermediary) { | ||||
| 					// No need to remap, as these are already intermediary | ||||
| 					remappedSignatures.set(mappingsProvider.getSignatureFixes()); | ||||
| 				} else { | ||||
| 					// Remap the sig fixes from intermediary to the target namespace | ||||
| 					final Map<String, String> remapped = new HashMap<>(); | ||||
| 					final TinyRemapper sigTinyRemapper = TinyRemapperHelper.getTinyRemapper(getProject(), MappingsNamespace.INTERMEDIARY.toString(), toM); | ||||
| 					final Remapper sigAsmRemapper = sigTinyRemapper.getRemapper(); | ||||
| 
 | ||||
| 					// Remap the class names and the signatures using a new tiny remapper instance. | ||||
| 					for (Map.Entry<String, String> entry : mappingsProvider.getSignatureFixes().entrySet()) { | ||||
| 						remapped.put( | ||||
| 								sigAsmRemapper.map(entry.getKey()), | ||||
| 								sigAsmRemapper.mapSignature(entry.getValue(), false) | ||||
| 						); | ||||
| 					} | ||||
| 
 | ||||
| 					sigTinyRemapper.finish(); | ||||
| 					remappedSignatures.set(remapped); | ||||
| 				} | ||||
| 			} | ||||
| 
 | ||||
| 			try (OutputConsumerPath outputConsumer = new OutputConsumerPath.Builder(output).build()) { | ||||
| 				outputConsumer.addNonClassFiles(input); | ||||
|  |  | |||
|  | @ -28,6 +28,7 @@ import java.io.File; | |||
| import java.io.IOException; | ||||
| import java.nio.file.Path; | ||||
| import java.util.Map; | ||||
| import java.util.function.Consumer; | ||||
| import java.util.regex.Pattern; | ||||
| 
 | ||||
| import com.google.common.collect.ImmutableMap; | ||||
|  | @ -59,10 +60,10 @@ public final class TinyRemapperHelper { | |||
| 	} | ||||
| 
 | ||||
| 	public static TinyRemapper getTinyRemapper(Project project, String fromM, String toM) throws IOException { | ||||
| 		return getTinyRemapper(project, fromM, toM, false); | ||||
| 		return getTinyRemapper(project, fromM, toM, false, (builder) -> { }); | ||||
| 	} | ||||
| 
 | ||||
| 	public static TinyRemapper getTinyRemapper(Project project, String fromM, String toM, boolean fixRecords) throws IOException { | ||||
| 	public static TinyRemapper getTinyRemapper(Project project, String fromM, String toM, boolean fixRecords, Consumer<TinyRemapper.Builder> builderConsumer) throws IOException { | ||||
| 		LoomGradleExtension extension = LoomGradleExtension.get(project); | ||||
| 		MemoryMappingTree mappingTree = extension.getMappingsProvider().getMappings(); | ||||
| 
 | ||||
|  | @ -72,7 +73,7 @@ public final class TinyRemapperHelper { | |||
| 
 | ||||
| 		int intermediaryNsId = mappingTree.getNamespaceId(MappingsNamespace.INTERMEDIARY.toString()); | ||||
| 
 | ||||
| 		return TinyRemapper.newRemapper() | ||||
| 		TinyRemapper.Builder builder = TinyRemapper.newRemapper() | ||||
| 				.withMappings(create(mappingTree, fromM, toM, true)) | ||||
| 				.withMappings(out -> JSR_TO_JETBRAINS.forEach(out::acceptClass)) | ||||
| 				.renameInvalidLocals(true) | ||||
|  | @ -84,8 +85,10 @@ public final class TinyRemapperHelper { | |||
| 					} | ||||
| 
 | ||||
| 					return next; | ||||
| 				}) | ||||
| 				.build(); | ||||
| 				}); | ||||
| 
 | ||||
| 		builderConsumer.accept(builder); | ||||
| 		return builder.build(); | ||||
| 	} | ||||
| 
 | ||||
| 	public static Path[] getMinecraftDependencies(Project project) { | ||||
|  |  | |||
|  | @ -26,7 +26,7 @@ package net.fabricmc.loom.test | |||
| 
 | ||||
| class LoomTestConstants { | ||||
|     public final static String DEFAULT_GRADLE = "7.0.1" | ||||
|     public final static String PRE_RELEASE_GRADLE = "7.3-20210906222431+0000" | ||||
|     public final static String PRE_RELEASE_GRADLE = "7.4-20210926222420+0000" | ||||
| 
 | ||||
|     public final static String[] STANDARD_TEST_VERSIONS = [DEFAULT_GRADLE, PRE_RELEASE_GRADLE] | ||||
| } | ||||
|  |  | |||
|  | @ -85,7 +85,7 @@ abstract class LayeredMappingsSpecification extends Specification implements Lay | |||
|     MemoryMappingTree getLayeredMappings(MappingsSpec<? extends MappingLayer>... specs) { | ||||
|         LayeredMappingSpec spec = new LayeredMappingSpec(specs.toList()) | ||||
|         LayeredMappingsProcessor processor = new LayeredMappingsProcessor(spec) | ||||
|         return processor.getMappings(mappingContext) | ||||
|         return processor.getMappings(processor.resolveLayers(mappingContext)) | ||||
|     } | ||||
| 
 | ||||
|     String getTiny(MemoryMappingTree mappingTree) { | ||||
|  |  | |||
|  | @ -10,12 +10,16 @@ archivesBaseName = project.archives_base_name | |||
| version = project.mod_version | ||||
| group = project.maven_group | ||||
| 
 | ||||
| loom { | ||||
| 	accessWidenerPath = file("src/main/resources/modid.accesswidener") | ||||
| } | ||||
| 
 | ||||
| dependencies { | ||||
| 	// To change the versions see the gradle.properties file | ||||
| 	minecraft "com.mojang:minecraft:${project.minecraft_version}" | ||||
| 	mappings "net.fabricmc:yarn:${project.yarn_mappings}:v2" | ||||
| 	modImplementation "net.fabricmc:fabric-loader:${project.loader_version}" | ||||
| 	//modImplementation "net.fabricmc.fabric-api:fabric-api:${project.fabric_version}" | ||||
| 	modImplementation "net.fabricmc.fabric-api:fabric-api:${project.fabric_version}" | ||||
| } | ||||
| 
 | ||||
| tasks.withType(JavaCompile).configureEach { | ||||
|  |  | |||
|  | @ -1,9 +1,9 @@ | |||
| org.gradle.jvmargs=-Xmx1G | ||||
| 
 | ||||
| minecraft_version=21w37a | ||||
| yarn_mappings=21w37a+build.5 | ||||
| minecraft_version=21w38a | ||||
| yarn_mappings=21w38a+build.9 | ||||
| loader_version=0.11.7 | ||||
| fabric_version=0.37.1+1.17 | ||||
| fabric_version=0.40.4+1.18 | ||||
| 
 | ||||
| mod_version = 1.0.0 | ||||
| maven_group = com.example | ||||
|  |  | |||
|  | @ -1,15 +1,11 @@ | |||
| package net.fabricmc.example.mixin; | ||||
| 
 | ||||
| import net.minecraft.client.gui.screen.TitleScreen; | ||||
| import net.minecraft.world.chunk.PalettedContainer; | ||||
| import org.spongepowered.asm.mixin.Mixin; | ||||
| import org.spongepowered.asm.mixin.injection.At; | ||||
| import org.spongepowered.asm.mixin.injection.Inject; | ||||
| import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; | ||||
| import org.spongepowered.asm.mixin.Shadow; | ||||
| 
 | ||||
| @Mixin(TitleScreen.class) | ||||
| public class ExampleMixin { | ||||
| 	@Inject(at = @At("HEAD"), method = "init()V") | ||||
| 	private void init(CallbackInfo info) { | ||||
| 		System.out.println("This line is printed by an example mod mixin!"); | ||||
| 	} | ||||
| @Mixin(PalettedContainer.class) | ||||
| public abstract class ExampleMixin<T> { | ||||
| 	@Shadow | ||||
| 	private volatile PalettedContainer.Data<T> data; | ||||
| } | ||||
|  | @ -32,5 +32,6 @@ | |||
|   }, | ||||
|   "suggests": { | ||||
|     "another-mod": "*" | ||||
|   } | ||||
|   }, | ||||
|   "accessWidener" : "modid.accesswidener" | ||||
| } | ||||
|  |  | |||
|  | @ -0,0 +1,3 @@ | |||
| accessWidener	v1	named | ||||
| 
 | ||||
| accessible    class    net/minecraft/world/chunk/PalettedContainer$Data | ||||
		Loading…
	
		Reference in a new issue