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); | 	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.LayeredMappingSpecBuilder; | ||||||
| import net.fabricmc.loom.api.mappings.layered.spec.MappingsSpec; | import net.fabricmc.loom.api.mappings.layered.spec.MappingsSpec; | ||||||
| import net.fabricmc.loom.api.mappings.layered.spec.ParchmentMappingsSpecBuilder; | 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.intermediary.IntermediaryMappingsSpec; | ||||||
| import net.fabricmc.loom.configuration.providers.mappings.mojmap.MojangMappingsSpec; | import net.fabricmc.loom.configuration.providers.mappings.mojmap.MojangMappingsSpec; | ||||||
| import net.fabricmc.loom.configuration.providers.mappings.parchment.ParchmentMappingsSpecBuilderImpl; | import net.fabricmc.loom.configuration.providers.mappings.parchment.ParchmentMappingsSpecBuilderImpl; | ||||||
|  | @ -59,6 +60,11 @@ public class LayeredMappingSpecBuilderImpl implements LayeredMappingSpecBuilder | ||||||
| 		return addLayer(builder.build()); | 		return addLayer(builder.build()); | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|  | 	@Override | ||||||
|  | 	public LayeredMappingSpecBuilder signatureFix(Object object) { | ||||||
|  | 		return addLayer(new SignatureFixesSpec(FileSpec.create(object))); | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
| 	public LayeredMappingSpec build() { | 	public LayeredMappingSpec build() { | ||||||
| 		List<MappingsSpec<?>> builtLayers = new LinkedList<>(); | 		List<MappingsSpec<?>> builtLayers = new LinkedList<>(); | ||||||
| 		// Intermediary is always the base layer | 		// Intermediary is always the base layer | ||||||
|  |  | ||||||
|  | @ -32,6 +32,8 @@ import java.nio.charset.StandardCharsets; | ||||||
| import java.nio.file.Files; | import java.nio.file.Files; | ||||||
| import java.nio.file.Path; | import java.nio.file.Path; | ||||||
| import java.util.Collections; | import java.util.Collections; | ||||||
|  | import java.util.List; | ||||||
|  | import java.util.Map; | ||||||
| import java.util.Objects; | import java.util.Objects; | ||||||
| import java.util.Set; | import java.util.Set; | ||||||
| 
 | 
 | ||||||
|  | @ -44,6 +46,7 @@ import org.zeroturnaround.zip.ZipUtil; | ||||||
| 
 | 
 | ||||||
| import net.fabricmc.loom.LoomGradlePlugin; | import net.fabricmc.loom.LoomGradlePlugin; | ||||||
| import net.fabricmc.loom.api.mappings.layered.MappingContext; | 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.MappingsNamespace; | ||||||
| import net.fabricmc.mappingio.adapter.MappingDstNsReorder; | import net.fabricmc.mappingio.adapter.MappingDstNsReorder; | ||||||
| import net.fabricmc.mappingio.adapter.MappingSourceNsSwitch; | import net.fabricmc.mappingio.adapter.MappingSourceNsSwitch; | ||||||
|  | @ -72,29 +75,51 @@ public class LayeredMappingsDependency implements SelfResolvingDependency { | ||||||
| 		if (!Files.exists(mappingsFile) || LoomGradlePlugin.refreshDeps) { | 		if (!Files.exists(mappingsFile) || LoomGradlePlugin.refreshDeps) { | ||||||
| 			try { | 			try { | ||||||
| 				var processor = new LayeredMappingsProcessor(layeredMappingSpec); | 				var processor = new LayeredMappingsProcessor(layeredMappingSpec); | ||||||
| 				MemoryMappingTree mappings = processor.getMappings(mappingContext); | 				List<MappingLayer> layers = processor.resolveLayers(mappingContext); | ||||||
| 
 | 
 | ||||||
| 				try (Writer writer = new StringWriter()) { | 				Files.deleteIfExists(mappingsFile); | ||||||
| 					Tiny2Writer tiny2Writer = new Tiny2Writer(writer, false); |  | ||||||
| 
 | 
 | ||||||
| 					MappingDstNsReorder nsReorder = new MappingDstNsReorder(tiny2Writer, Collections.singletonList(MappingsNamespace.NAMED.toString())); | 				writeMapping(processor, layers, mappingsFile); | ||||||
| 					MappingSourceNsSwitch nsSwitch = new MappingSourceNsSwitch(nsReorder, MappingsNamespace.INTERMEDIARY.toString(), true); | 				writeSignatureFixes(processor, layers, mappingsFile); | ||||||
| 					mappings.accept(nsSwitch); |  | ||||||
| 
 |  | ||||||
| 					Files.deleteIfExists(mappingsFile); |  | ||||||
| 
 |  | ||||||
| 					ZipUtil.pack(new ZipEntrySource[] { |  | ||||||
| 							new ByteSource("mappings/mappings.tiny", writer.toString().getBytes(StandardCharsets.UTF_8)) |  | ||||||
| 					}, mappingsFile.toFile()); |  | ||||||
| 				} |  | ||||||
| 			} catch (IOException e) { | 			} 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()); | 		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 | 	@Override | ||||||
| 	public Set<File> resolve(boolean transitive) { | 	public Set<File> resolve(boolean transitive) { | ||||||
| 		return resolve(); | 		return resolve(); | ||||||
|  |  | ||||||
|  | @ -26,12 +26,19 @@ package net.fabricmc.loom.configuration.providers.mappings; | ||||||
| 
 | 
 | ||||||
| import java.io.IOException; | import java.io.IOException; | ||||||
| import java.util.ArrayList; | import java.util.ArrayList; | ||||||
|  | import java.util.Collections; | ||||||
|  | import java.util.HashMap; | ||||||
|  | import java.util.LinkedList; | ||||||
| import java.util.List; | 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.MappingContext; | ||||||
| import net.fabricmc.loom.api.mappings.layered.MappingLayer; | import net.fabricmc.loom.api.mappings.layered.MappingLayer; | ||||||
| import net.fabricmc.loom.api.mappings.layered.MappingsNamespace; | import net.fabricmc.loom.api.mappings.layered.MappingsNamespace; | ||||||
| import net.fabricmc.loom.api.mappings.layered.spec.MappingsSpec; | 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.adapter.MappingSourceNsSwitch; | ||||||
| import net.fabricmc.mappingio.tree.MemoryMappingTree; | import net.fabricmc.mappingio.tree.MemoryMappingTree; | ||||||
| 
 | 
 | ||||||
|  | @ -42,9 +49,8 @@ public class LayeredMappingsProcessor { | ||||||
| 		this.layeredMappingSpec = spec; | 		this.layeredMappingSpec = spec; | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	public MemoryMappingTree getMappings(MappingContext context) throws IOException { | 	public List<MappingLayer> resolveLayers(MappingContext context) { | ||||||
| 		MemoryMappingTree mappingTree = new MemoryMappingTree(); | 		List<MappingLayer> layers = new LinkedList<>(); | ||||||
| 
 |  | ||||||
| 		List<Class<? extends MappingLayer>> visitedLayers = new ArrayList<>(); | 		List<Class<? extends MappingLayer>> visitedLayers = new ArrayList<>(); | ||||||
| 
 | 
 | ||||||
| 		for (MappingsSpec<?> spec : layeredMappingSpec.layers()) { | 		for (MappingsSpec<?> spec : layeredMappingSpec.layers()) { | ||||||
|  | @ -56,8 +62,17 @@ public class LayeredMappingsProcessor { | ||||||
| 				} | 				} | ||||||
| 			} | 			} | ||||||
| 
 | 
 | ||||||
|  | 			layers.add(layer); | ||||||
| 			visitedLayers.add(layer.getClass()); | 			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 | 			// We have to rebuild a new tree to work on when a layer doesnt merge into layered | ||||||
| 			boolean rebuild = layer.getSourceNamespace() != MappingsNamespace.NAMED; | 			boolean rebuild = layer.getSourceNamespace() != MappingsNamespace.NAMED; | ||||||
| 			MemoryMappingTree workingTree; | 			MemoryMappingTree workingTree; | ||||||
|  | @ -90,4 +105,21 @@ public class LayeredMappingsProcessor { | ||||||
| 
 | 
 | ||||||
| 		return mappingTree; | 		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.BufferedReader; | ||||||
| import java.io.File; | import java.io.File; | ||||||
| import java.io.IOException; | import java.io.IOException; | ||||||
|  | import java.io.Reader; | ||||||
| import java.net.URL; | import java.net.URL; | ||||||
| import java.nio.charset.StandardCharsets; | import java.nio.charset.StandardCharsets; | ||||||
| import java.nio.file.FileSystem; | import java.nio.file.FileSystem; | ||||||
|  | @ -37,6 +38,7 @@ import java.nio.file.StandardCopyOption; | ||||||
| import java.util.Arrays; | import java.util.Arrays; | ||||||
| import java.util.Collections; | import java.util.Collections; | ||||||
| import java.util.Objects; | import java.util.Objects; | ||||||
|  | import java.util.Map; | ||||||
| import java.util.function.Consumer; | import java.util.function.Consumer; | ||||||
| import java.util.regex.Pattern; | import java.util.regex.Pattern; | ||||||
| 
 | 
 | ||||||
|  | @ -45,6 +47,7 @@ import com.google.common.net.UrlEscapers; | ||||||
| import com.google.gson.JsonObject; | import com.google.gson.JsonObject; | ||||||
| import org.apache.tools.ant.util.StringUtils; | import org.apache.tools.ant.util.StringUtils; | ||||||
| import org.gradle.api.Project; | import org.gradle.api.Project; | ||||||
|  | import org.jetbrains.annotations.Nullable; | ||||||
| import org.zeroturnaround.zip.FileSource; | import org.zeroturnaround.zip.FileSource; | ||||||
| import org.zeroturnaround.zip.ZipEntrySource; | import org.zeroturnaround.zip.ZipEntrySource; | ||||||
| import org.zeroturnaround.zip.ZipUtil; | import org.zeroturnaround.zip.ZipUtil; | ||||||
|  | @ -92,6 +95,7 @@ public class MappingsProviderImpl extends DependencyProvider implements Mappings | ||||||
| 	private boolean hasUnpickDefinitions; | 	private boolean hasUnpickDefinitions; | ||||||
| 	private UnpickMetadata unpickMetadata; | 	private UnpickMetadata unpickMetadata; | ||||||
| 	private MemoryMappingTree mappingTree; | 	private MemoryMappingTree mappingTree; | ||||||
|  | 	private Map<String, String> signatureFixes; | ||||||
| 
 | 
 | ||||||
| 	public MappingsProviderImpl(Project project) { | 	public MappingsProviderImpl(Project project) { | ||||||
| 		super(project); | 		super(project); | ||||||
|  | @ -120,7 +124,7 @@ public class MappingsProviderImpl extends DependencyProvider implements Mappings | ||||||
| 			storeMappings(getProject(), minecraftProvider, mappingsJar.toPath()); | 			storeMappings(getProject(), minecraftProvider, mappingsJar.toPath()); | ||||||
| 		} else { | 		} else { | ||||||
| 			try (FileSystem fileSystem = FileSystems.newFileSystem(mappingsJar.toPath(), (ClassLoader) null)) { | 			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)) { | 		try (FileSystem fileSystem = FileSystems.newFileSystem(yarnJar, (ClassLoader) null)) { | ||||||
| 			extractMappings(fileSystem, baseTinyMappings); | 			extractMappings(fileSystem, baseTinyMappings); | ||||||
| 			extractUnpickDefinitions(fileSystem, unpickDefinitions); | 			extractExtras(fileSystem); | ||||||
| 		} | 		} | ||||||
| 
 | 
 | ||||||
| 		if (areMappingsV2(baseTinyMappings)) { | 		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); | 		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 unpickPath = jar.getPath("extras/definitions.unpick"); | ||||||
| 		Path unpickMetadataPath = jar.getPath("extras/unpick.json"); | 		Path unpickMetadataPath = jar.getPath("extras/unpick.json"); | ||||||
| 
 | 
 | ||||||
|  | @ -261,12 +270,25 @@ public class MappingsProviderImpl extends DependencyProvider implements Mappings | ||||||
| 			return; | 			return; | ||||||
| 		} | 		} | ||||||
| 
 | 
 | ||||||
| 		Files.copy(unpickPath, extractTo, StandardCopyOption.REPLACE_EXISTING); | 		Files.copy(unpickPath, unpickDefinitions, StandardCopyOption.REPLACE_EXISTING); | ||||||
| 
 | 
 | ||||||
| 		unpickMetadata = parseUnpickMetadata(unpickMetadataPath); | 		unpickMetadata = parseUnpickMetadata(unpickMetadataPath); | ||||||
| 		hasUnpickDefinitions = true; | 		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 { | 	private UnpickMetadata parseUnpickMetadata(Path input) throws IOException { | ||||||
| 		JsonObject jsonObject = LoomGradlePlugin.GSON.fromJson(Files.readString(input), JsonObject.class); | 		JsonObject jsonObject = LoomGradlePlugin.GSON.fromJson(Files.readString(input), JsonObject.class); | ||||||
| 
 | 
 | ||||||
|  | @ -461,6 +483,11 @@ public class MappingsProviderImpl extends DependencyProvider implements Mappings | ||||||
| 		return hasUnpickDefinitions; | 		return hasUnpickDefinitions; | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|  | 	@Nullable | ||||||
|  | 	public Map<String, String> getSignatureFixes() { | ||||||
|  | 		return signatureFixes; | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
| 	@Override | 	@Override | ||||||
| 	public File intermediaryTinyFile() { | 	public File intermediaryTinyFile() { | ||||||
| 		try { | 		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.Files; | ||||||
| import java.nio.file.Path; | import java.nio.file.Path; | ||||||
| import java.util.Arrays; | 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 java.util.function.Consumer; | ||||||
| 
 | 
 | ||||||
| import org.gradle.api.Project; | 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.api.mappings.layered.MappingsNamespace; | ||||||
| import net.fabricmc.loom.configuration.DependencyProvider; | 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.loom.util.TinyRemapperHelper; | ||||||
| import net.fabricmc.tinyremapper.OutputConsumerPath; | import net.fabricmc.tinyremapper.OutputConsumerPath; | ||||||
| import net.fabricmc.tinyremapper.TinyRemapper; | import net.fabricmc.tinyremapper.TinyRemapper; | ||||||
|  | import net.fabricmc.tinyremapper.api.TrClass; | ||||||
| 
 | 
 | ||||||
| public class MinecraftMappedProvider extends DependencyProvider { | public class MinecraftMappedProvider extends DependencyProvider { | ||||||
| 	private File minecraftMappedJar; | 	private File minecraftMappedJar; | ||||||
|  | @ -101,13 +108,67 @@ public class MinecraftMappedProvider extends DependencyProvider { | ||||||
| 		Path outputIntermediary = minecraftIntermediaryJar.toPath(); | 		Path outputIntermediary = minecraftIntermediaryJar.toPath(); | ||||||
| 
 | 
 | ||||||
| 		for (String toM : Arrays.asList(MappingsNamespace.NAMED.toString(), MappingsNamespace.INTERMEDIARY.toString())) { | 		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 + ")"); | 			getProject().getLogger().lifecycle(":remapping minecraft (TinyRemapper, " + fromM + " -> " + toM + ")"); | ||||||
| 
 | 
 | ||||||
| 			Files.deleteIfExists(output); | 			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()) { | 			try (OutputConsumerPath outputConsumer = new OutputConsumerPath.Builder(output).build()) { | ||||||
| 				outputConsumer.addNonClassFiles(input); | 				outputConsumer.addNonClassFiles(input); | ||||||
|  |  | ||||||
|  | @ -28,6 +28,7 @@ import java.io.File; | ||||||
| import java.io.IOException; | import java.io.IOException; | ||||||
| import java.nio.file.Path; | import java.nio.file.Path; | ||||||
| import java.util.Map; | import java.util.Map; | ||||||
|  | import java.util.function.Consumer; | ||||||
| import java.util.regex.Pattern; | import java.util.regex.Pattern; | ||||||
| 
 | 
 | ||||||
| import com.google.common.collect.ImmutableMap; | 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 { | 	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); | 		LoomGradleExtension extension = LoomGradleExtension.get(project); | ||||||
| 		MemoryMappingTree mappingTree = extension.getMappingsProvider().getMappings(); | 		MemoryMappingTree mappingTree = extension.getMappingsProvider().getMappings(); | ||||||
| 
 | 
 | ||||||
|  | @ -72,7 +73,7 @@ public final class TinyRemapperHelper { | ||||||
| 
 | 
 | ||||||
| 		int intermediaryNsId = mappingTree.getNamespaceId(MappingsNamespace.INTERMEDIARY.toString()); | 		int intermediaryNsId = mappingTree.getNamespaceId(MappingsNamespace.INTERMEDIARY.toString()); | ||||||
| 
 | 
 | ||||||
| 		return TinyRemapper.newRemapper() | 		TinyRemapper.Builder builder = TinyRemapper.newRemapper() | ||||||
| 				.withMappings(create(mappingTree, fromM, toM, true)) | 				.withMappings(create(mappingTree, fromM, toM, true)) | ||||||
| 				.withMappings(out -> JSR_TO_JETBRAINS.forEach(out::acceptClass)) | 				.withMappings(out -> JSR_TO_JETBRAINS.forEach(out::acceptClass)) | ||||||
| 				.renameInvalidLocals(true) | 				.renameInvalidLocals(true) | ||||||
|  | @ -84,8 +85,10 @@ public final class TinyRemapperHelper { | ||||||
| 					} | 					} | ||||||
| 
 | 
 | ||||||
| 					return next; | 					return next; | ||||||
| 				}) | 				}); | ||||||
| 				.build(); | 
 | ||||||
|  | 		builderConsumer.accept(builder); | ||||||
|  | 		return builder.build(); | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	public static Path[] getMinecraftDependencies(Project project) { | 	public static Path[] getMinecraftDependencies(Project project) { | ||||||
|  |  | ||||||
|  | @ -26,7 +26,7 @@ package net.fabricmc.loom.test | ||||||
| 
 | 
 | ||||||
| class LoomTestConstants { | class LoomTestConstants { | ||||||
|     public final static String DEFAULT_GRADLE = "7.0.1" |     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] |     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) { |     MemoryMappingTree getLayeredMappings(MappingsSpec<? extends MappingLayer>... specs) { | ||||||
|         LayeredMappingSpec spec = new LayeredMappingSpec(specs.toList()) |         LayeredMappingSpec spec = new LayeredMappingSpec(specs.toList()) | ||||||
|         LayeredMappingsProcessor processor = new LayeredMappingsProcessor(spec) |         LayeredMappingsProcessor processor = new LayeredMappingsProcessor(spec) | ||||||
|         return processor.getMappings(mappingContext) |         return processor.getMappings(processor.resolveLayers(mappingContext)) | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     String getTiny(MemoryMappingTree mappingTree) { |     String getTiny(MemoryMappingTree mappingTree) { | ||||||
|  |  | ||||||
|  | @ -10,12 +10,16 @@ archivesBaseName = project.archives_base_name | ||||||
| version = project.mod_version | version = project.mod_version | ||||||
| group = project.maven_group | group = project.maven_group | ||||||
| 
 | 
 | ||||||
|  | loom { | ||||||
|  | 	accessWidenerPath = file("src/main/resources/modid.accesswidener") | ||||||
|  | } | ||||||
|  | 
 | ||||||
| dependencies { | dependencies { | ||||||
| 	// To change the versions see the gradle.properties file | 	// To change the versions see the gradle.properties file | ||||||
| 	minecraft "com.mojang:minecraft:${project.minecraft_version}" | 	minecraft "com.mojang:minecraft:${project.minecraft_version}" | ||||||
| 	mappings "net.fabricmc:yarn:${project.yarn_mappings}:v2" | 	mappings "net.fabricmc:yarn:${project.yarn_mappings}:v2" | ||||||
| 	modImplementation "net.fabricmc:fabric-loader:${project.loader_version}" | 	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 { | tasks.withType(JavaCompile).configureEach { | ||||||
|  |  | ||||||
|  | @ -1,9 +1,9 @@ | ||||||
| org.gradle.jvmargs=-Xmx1G | org.gradle.jvmargs=-Xmx1G | ||||||
| 
 | 
 | ||||||
| minecraft_version=21w37a | minecraft_version=21w38a | ||||||
| yarn_mappings=21w37a+build.5 | yarn_mappings=21w38a+build.9 | ||||||
| loader_version=0.11.7 | loader_version=0.11.7 | ||||||
| fabric_version=0.37.1+1.17 | fabric_version=0.40.4+1.18 | ||||||
| 
 | 
 | ||||||
| mod_version = 1.0.0 | mod_version = 1.0.0 | ||||||
| maven_group = com.example | maven_group = com.example | ||||||
|  |  | ||||||
|  | @ -1,15 +1,11 @@ | ||||||
| package net.fabricmc.example.mixin; | 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.Mixin; | ||||||
| import org.spongepowered.asm.mixin.injection.At; | import org.spongepowered.asm.mixin.Shadow; | ||||||
| import org.spongepowered.asm.mixin.injection.Inject; |  | ||||||
| import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; |  | ||||||
| 
 | 
 | ||||||
| @Mixin(TitleScreen.class) | @Mixin(PalettedContainer.class) | ||||||
| public class ExampleMixin { | public abstract class ExampleMixin<T> { | ||||||
| 	@Inject(at = @At("HEAD"), method = "init()V") | 	@Shadow | ||||||
| 	private void init(CallbackInfo info) { | 	private volatile PalettedContainer.Data<T> data; | ||||||
| 		System.out.println("This line is printed by an example mod mixin!"); | } | ||||||
| 	} |  | ||||||
| } |  | ||||||
|  | @ -32,5 +32,6 @@ | ||||||
|   }, |   }, | ||||||
|   "suggests": { |   "suggests": { | ||||||
|     "another-mod": "*" |     "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