mirror of
https://github.com/architectury/architectury-loom.git
synced 2026-03-28 04:07:01 -05:00
Use MCPConfig data for processing Forge jar, support single-jar Forge (#87)
* Use MCPConfig data for merging and remapping Forge jar * Make DependencyDownloader support multiple deps * Support server-/client-only Minecraft with Forge There's one slight caveat here: the server jar contains some client-only classes that have been patched. This also happens with the official Forge installer in production, so it's probably fine. * Remove binpatcher dep * Move McpConfigProvider to correct package * Print tool name for functions in McpExecutor * Fix Forge tools outputting verbose output at IDEA refresh * Fix certain Forge deps being excluded from run configs * Always produce the client extra jar * Add step count to McpExecutor logging * Allow missing args and jvmargs in MCP functions This should fix using 1.14.4 and 1.16.5, which don't have JVM args for everything. * Make MCP function downloads follow redirects * Refactor MCP step outputs, don't copy raw MC jars * Remove MinecraftProviderBridge
This commit is contained in:
@@ -125,7 +125,6 @@ dependencies {
|
||||
|
||||
// Forge patches
|
||||
implementation ('net.minecraftforge:installertools:1.2.0')
|
||||
implementation ('net.minecraftforge:binarypatcher:1.1.1')
|
||||
implementation ('org.cadixdev:lorenz:0.5.3')
|
||||
implementation ('org.cadixdev:lorenz-asm:0.5.3')
|
||||
implementation ('de.oceanlabs.mcp:mcinjector:3.8.0')
|
||||
|
||||
@@ -47,9 +47,9 @@ import net.fabricmc.loom.configuration.providers.forge.DependencyProviders;
|
||||
import net.fabricmc.loom.configuration.providers.forge.ForgeProvider;
|
||||
import net.fabricmc.loom.configuration.providers.forge.ForgeUniversalProvider;
|
||||
import net.fabricmc.loom.configuration.providers.forge.ForgeUserdevProvider;
|
||||
import net.fabricmc.loom.configuration.providers.forge.McpConfigProvider;
|
||||
import net.fabricmc.loom.configuration.providers.forge.PatchProvider;
|
||||
import net.fabricmc.loom.configuration.providers.forge.SrgProvider;
|
||||
import net.fabricmc.loom.configuration.providers.forge.mcpconfig.McpConfigProvider;
|
||||
import net.fabricmc.loom.configuration.providers.mappings.MappingsProviderImpl;
|
||||
import net.fabricmc.loom.configuration.providers.minecraft.MinecraftProvider;
|
||||
import net.fabricmc.loom.configuration.providers.minecraft.mapped.IntermediaryMinecraftProvider;
|
||||
|
||||
@@ -55,10 +55,10 @@ import net.fabricmc.loom.configuration.providers.forge.DependencyProviders;
|
||||
import net.fabricmc.loom.configuration.providers.forge.ForgeProvider;
|
||||
import net.fabricmc.loom.configuration.providers.forge.ForgeUniversalProvider;
|
||||
import net.fabricmc.loom.configuration.providers.forge.ForgeUserdevProvider;
|
||||
import net.fabricmc.loom.configuration.providers.forge.McpConfigProvider;
|
||||
import net.fabricmc.loom.configuration.providers.forge.MinecraftPatchedProvider;
|
||||
import net.fabricmc.loom.configuration.providers.forge.PatchProvider;
|
||||
import net.fabricmc.loom.configuration.providers.forge.SrgProvider;
|
||||
import net.fabricmc.loom.configuration.providers.forge.mcpconfig.McpConfigProvider;
|
||||
import net.fabricmc.loom.configuration.providers.forge.minecraft.ForgeMinecraftProvider;
|
||||
import net.fabricmc.loom.configuration.providers.mappings.MappingsProviderImpl;
|
||||
import net.fabricmc.loom.configuration.providers.minecraft.MinecraftJarConfiguration;
|
||||
import net.fabricmc.loom.configuration.providers.minecraft.MinecraftProvider;
|
||||
@@ -276,8 +276,8 @@ public final class CompileConfiguration {
|
||||
// Provide the vanilla mc jars -- TODO share across projects.
|
||||
final MinecraftProvider minecraftProvider = jarConfiguration.getMinecraftProviderFunction().apply(project);
|
||||
|
||||
if (extension.isForge() && !(minecraftProvider instanceof MinecraftPatchedProvider)) {
|
||||
throw new UnsupportedOperationException("Using Forge with split or server-only jars is not currently supported!");
|
||||
if (extension.isForge() && !(minecraftProvider instanceof ForgeMinecraftProvider)) {
|
||||
throw new UnsupportedOperationException("Using Forge with split jars is not supported!");
|
||||
}
|
||||
|
||||
extension.setMinecraftProvider(minecraftProvider);
|
||||
@@ -288,8 +288,8 @@ public final class CompileConfiguration {
|
||||
extension.setMappingsProvider(mappingsProvider);
|
||||
mappingsProvider.applyToProject(project, mappingsDep);
|
||||
|
||||
if (minecraftProvider instanceof MinecraftPatchedProvider patched) {
|
||||
patched.remapJar();
|
||||
if (minecraftProvider instanceof ForgeMinecraftProvider patched) {
|
||||
patched.getPatchedProvider().remapJar();
|
||||
}
|
||||
|
||||
// Provide the remapped mc jars
|
||||
|
||||
@@ -165,8 +165,10 @@ public final class AccessTransformerJarProcessor implements JarProcessor {
|
||||
|
||||
public static void executeAt(Project project, Path input, Path output, AccessTransformerConfiguration configuration) throws IOException {
|
||||
boolean serverBundleMetadataPresent = LoomGradleExtension.get(project).getMinecraftProvider().getServerBundleMetadata() != null;
|
||||
String atDependency = Constants.Dependencies.ACCESS_TRANSFORMERS + (serverBundleMetadataPresent ? Constants.Dependencies.Versions.ACCESS_TRANSFORMERS_NEW : Constants.Dependencies.Versions.ACCESS_TRANSFORMERS);
|
||||
FileCollection classpath = DependencyDownloader.download(project, atDependency);
|
||||
FileCollection classpath = new DependencyDownloader(project)
|
||||
.add(Constants.Dependencies.ACCESS_TRANSFORMERS + (serverBundleMetadataPresent ? Constants.Dependencies.Versions.ACCESS_TRANSFORMERS_NEW : Constants.Dependencies.Versions.ACCESS_TRANSFORMERS))
|
||||
.add(Constants.Dependencies.ASM + Constants.Dependencies.Versions.ASM)
|
||||
.download();
|
||||
List<String> args = new ArrayList<>();
|
||||
args.add("--inJar");
|
||||
args.add(input.toAbsolutePath().toString());
|
||||
|
||||
@@ -25,6 +25,7 @@
|
||||
package net.fabricmc.loom.configuration.providers.forge;
|
||||
|
||||
import java.io.File;
|
||||
import java.nio.file.Path;
|
||||
|
||||
import org.gradle.api.Project;
|
||||
import org.gradle.api.artifacts.Dependency;
|
||||
@@ -53,7 +54,7 @@ public abstract class DependencyProvider {
|
||||
}
|
||||
|
||||
static Dependency addDependency(Project project, Object object, String target) {
|
||||
if (object instanceof File) {
|
||||
if (object instanceof File || object instanceof Path) {
|
||||
object = project.files(object);
|
||||
}
|
||||
|
||||
|
||||
@@ -25,7 +25,6 @@
|
||||
package net.fabricmc.loom.configuration.providers.forge;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.io.StringWriter;
|
||||
import java.io.UncheckedIOException;
|
||||
@@ -203,19 +202,16 @@ public class FieldMigratedMappingsProvider extends MappingsProviderImpl {
|
||||
}
|
||||
|
||||
Visitor visitor = new Visitor(Opcodes.ASM9);
|
||||
Path patchedSrgJar = MinecraftPatchedProvider.get(project).getMinecraftPatchedSrgJar();
|
||||
FileSystemUtil.Delegate system = FileSystemUtil.getJarFileSystem(patchedSrgJar, false);
|
||||
completer.onComplete(value -> system.close());
|
||||
|
||||
for (MinecraftPatchedProvider.Environment environment : MinecraftPatchedProvider.Environment.values()) {
|
||||
File patchedSrgJar = environment.patchedSrgJar.apply(MinecraftPatchedProvider.get(project));
|
||||
FileSystemUtil.Delegate system = FileSystemUtil.getJarFileSystem(patchedSrgJar, false);
|
||||
completer.onComplete(value -> system.close());
|
||||
|
||||
for (Path fsPath : (Iterable<? extends Path>) Files.walk(system.get().getPath("/"))::iterator) {
|
||||
if (Files.isRegularFile(fsPath) && fsPath.toString().endsWith(".class")) {
|
||||
completer.add(() -> {
|
||||
byte[] bytes = Files.readAllBytes(fsPath);
|
||||
new ClassReader(bytes).accept(visitor, ClassReader.SKIP_CODE | ClassReader.SKIP_DEBUG | ClassReader.SKIP_FRAMES);
|
||||
});
|
||||
}
|
||||
for (Path fsPath : (Iterable<? extends Path>) Files.walk(system.get().getPath("/"))::iterator) {
|
||||
if (Files.isRegularFile(fsPath) && fsPath.toString().endsWith(".class")) {
|
||||
completer.add(() -> {
|
||||
byte[] bytes = Files.readAllBytes(fsPath);
|
||||
new ClassReader(bytes).accept(visitor, ClassReader.SKIP_CODE | ClassReader.SKIP_DEBUG | ClassReader.SKIP_FRAMES);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
* This file is part of fabric-loom, licensed under the MIT License (MIT).
|
||||
*
|
||||
* Copyright (c) 2020-2021 FabricMC
|
||||
* Copyright (c) 2020-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
|
||||
@@ -58,6 +58,7 @@ import org.gradle.api.attributes.Attribute;
|
||||
import org.gradle.api.file.FileSystemLocation;
|
||||
import org.gradle.api.provider.Provider;
|
||||
|
||||
import net.fabricmc.loom.LoomGradlePlugin;
|
||||
import net.fabricmc.loom.api.ForgeLocalMod;
|
||||
import net.fabricmc.loom.configuration.DependencyInfo;
|
||||
import net.fabricmc.loom.configuration.ide.RunConfigSettings;
|
||||
@@ -66,10 +67,13 @@ import net.fabricmc.loom.util.Constants;
|
||||
import net.fabricmc.loom.util.DependencyDownloader;
|
||||
import net.fabricmc.loom.util.FileSystemUtil;
|
||||
import net.fabricmc.loom.util.PropertyUtil;
|
||||
import net.fabricmc.loom.util.ZipUtils;
|
||||
|
||||
public class ForgeUserdevProvider extends DependencyProvider {
|
||||
private File userdevJar;
|
||||
private JsonObject json;
|
||||
Path joinedPatches;
|
||||
BinaryPatcherConfig binaryPatcherConfig;
|
||||
|
||||
public ForgeUserdevProvider(Project project) {
|
||||
super(project);
|
||||
@@ -89,6 +93,7 @@ public class ForgeUserdevProvider extends DependencyProvider {
|
||||
}
|
||||
|
||||
userdevJar = new File(getExtension().getForgeProvider().getGlobalCache(), "forge-userdev.jar");
|
||||
joinedPatches = getExtension().getForgeProvider().getGlobalCache().toPath().resolve("patches-joined.lzma");
|
||||
Path configJson = getExtension().getForgeProvider().getGlobalCache().toPath().resolve("forge-config.json");
|
||||
|
||||
if (!userdevJar.exists() || Files.notExists(configJson) || isRefreshDeps()) {
|
||||
@@ -132,8 +137,12 @@ public class ForgeUserdevProvider extends DependencyProvider {
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: Should I copy the patches from here as well?
|
||||
// That'd require me to run the "MCP environment" fully up to merging.
|
||||
if (Files.notExists(joinedPatches)) {
|
||||
Files.write(joinedPatches, ZipUtils.unpack(userdevJar.toPath(), json.get("binpatches").getAsString()));
|
||||
}
|
||||
|
||||
binaryPatcherConfig = BinaryPatcherConfig.fromJson(json.getAsJsonObject("binpatcher"));
|
||||
|
||||
for (Map.Entry<String, JsonElement> entry : json.getAsJsonObject("runs").entrySet()) {
|
||||
LaunchProviderSettings launchSettings = getExtension().getLaunchConfigs().findByName(entry.getKey());
|
||||
RunConfigSettings settings = getExtension().getRunConfigs().findByName(entry.getKey());
|
||||
@@ -307,4 +316,12 @@ public class ForgeUserdevProvider extends DependencyProvider {
|
||||
public String getTargetConfig() {
|
||||
return Constants.Configurations.FORGE_USERDEV;
|
||||
}
|
||||
|
||||
public record BinaryPatcherConfig(String dependency, List<String> args) {
|
||||
public static BinaryPatcherConfig fromJson(JsonObject json) {
|
||||
String dependency = json.get("version").getAsString();
|
||||
List<String> args = List.of(LoomGradlePlugin.GSON.fromJson(json.get("args"), String[].class));
|
||||
return new BinaryPatcherConfig(dependency, args);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,231 +0,0 @@
|
||||
/*
|
||||
* This file is part of fabric-loom, licensed under the MIT License (MIT).
|
||||
*
|
||||
* Copyright (c) 2020-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.forge;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.io.Reader;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.StandardCopyOption;
|
||||
import java.nio.file.StandardOpenOption;
|
||||
import java.util.List;
|
||||
import java.util.jar.Attributes;
|
||||
import java.util.jar.Manifest;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.StreamSupport;
|
||||
|
||||
import com.google.gson.Gson;
|
||||
import com.google.gson.JsonElement;
|
||||
import com.google.gson.JsonObject;
|
||||
import org.gradle.api.Project;
|
||||
import org.gradle.api.file.FileCollection;
|
||||
|
||||
import net.fabricmc.loom.configuration.DependencyInfo;
|
||||
import net.fabricmc.loom.util.Constants;
|
||||
import net.fabricmc.loom.util.DependencyDownloader;
|
||||
import net.fabricmc.loom.util.ZipUtils;
|
||||
|
||||
public class McpConfigProvider extends DependencyProvider {
|
||||
private Path mcp;
|
||||
private Path configJson;
|
||||
private Path mappings;
|
||||
private Boolean official;
|
||||
private String mappingsPath;
|
||||
private RemapAction remapAction;
|
||||
|
||||
public McpConfigProvider(Project project) {
|
||||
super(project);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void provide(DependencyInfo dependency) throws Exception {
|
||||
init(dependency.getDependency().getVersion());
|
||||
|
||||
Path mcpZip = dependency.resolveFile().orElseThrow(() -> new RuntimeException("Could not resolve MCPConfig")).toPath();
|
||||
|
||||
if (!Files.exists(mcp) || !Files.exists(configJson) || isRefreshDeps()) {
|
||||
Files.copy(mcpZip, mcp, StandardCopyOption.REPLACE_EXISTING);
|
||||
Files.write(configJson, ZipUtils.unpack(mcp, "config.json"), StandardOpenOption.CREATE, StandardOpenOption.TRUNCATE_EXISTING);
|
||||
}
|
||||
|
||||
JsonObject json;
|
||||
|
||||
try (Reader reader = Files.newBufferedReader(configJson)) {
|
||||
json = new Gson().fromJson(reader, JsonObject.class);
|
||||
}
|
||||
|
||||
official = json.has("official") && json.getAsJsonPrimitive("official").getAsBoolean();
|
||||
mappingsPath = json.get("data").getAsJsonObject().get("mappings").getAsString();
|
||||
|
||||
if (json.has("functions")) {
|
||||
JsonObject functions = json.getAsJsonObject("functions");
|
||||
|
||||
if (functions.has("rename")) {
|
||||
remapAction = new ConfigDefinedRemapAction(getProject(), functions.getAsJsonObject("rename"));
|
||||
}
|
||||
}
|
||||
|
||||
if (remapAction == null) {
|
||||
throw new RuntimeException("Could not find remap action, this is probably a version Architectury Loom does not support!");
|
||||
}
|
||||
}
|
||||
|
||||
public RemapAction getRemapAction() {
|
||||
return remapAction;
|
||||
}
|
||||
|
||||
private void init(String version) throws IOException {
|
||||
Path dir = getMinecraftProvider().dir("mcp/" + version).toPath();
|
||||
mcp = dir.resolve("mcp.zip");
|
||||
configJson = dir.resolve("mcp-config.json");
|
||||
mappings = dir.resolve("mcp-config-mappings.txt");
|
||||
|
||||
if (isRefreshDeps()) {
|
||||
Files.deleteIfExists(mappings);
|
||||
}
|
||||
}
|
||||
|
||||
public Path getMappings() {
|
||||
if (Files.notExists(mappings)) {
|
||||
try {
|
||||
Files.write(mappings, ZipUtils.unpack(getMcp(), getMappingsPath()), StandardOpenOption.CREATE, StandardOpenOption.TRUNCATE_EXISTING);
|
||||
} catch (IOException e) {
|
||||
throw new IllegalStateException("Failed to find mappings '" + getMappingsPath() + "' in " + getMcp() + "!");
|
||||
}
|
||||
}
|
||||
|
||||
return mappings;
|
||||
}
|
||||
|
||||
public Path getMcp() {
|
||||
return mcp;
|
||||
}
|
||||
|
||||
public boolean isOfficial() {
|
||||
return official;
|
||||
}
|
||||
|
||||
public String getMappingsPath() {
|
||||
return mappingsPath;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getTargetConfig() {
|
||||
return Constants.Configurations.MCP_CONFIG;
|
||||
}
|
||||
|
||||
public interface RemapAction {
|
||||
FileCollection getClasspath();
|
||||
|
||||
String getMainClass();
|
||||
|
||||
List<String> getArgs(Path input, Path output, Path mappings, FileCollection libraries);
|
||||
}
|
||||
|
||||
public static class ConfigDefinedRemapAction implements RemapAction {
|
||||
private final Project project;
|
||||
private final String name;
|
||||
private final File mainClasspath;
|
||||
private final FileCollection classpath;
|
||||
private final List<String> args;
|
||||
private boolean hasLibraries;
|
||||
|
||||
public ConfigDefinedRemapAction(Project project, JsonObject json) {
|
||||
this.project = project;
|
||||
this.name = json.get("version").getAsString();
|
||||
this.mainClasspath = DependencyDownloader.download(project, this.name, false, true)
|
||||
.getSingleFile();
|
||||
this.classpath = DependencyDownloader.download(project, this.name, true, true);
|
||||
this.args = StreamSupport.stream(json.getAsJsonArray("args").spliterator(), false)
|
||||
.map(JsonElement::getAsString)
|
||||
.collect(Collectors.toList());
|
||||
for (int i = 1; i < this.args.size(); i++) {
|
||||
if (this.args.get(i).equals("{libraries}")) {
|
||||
this.args.remove(i);
|
||||
this.args.remove(i - 1);
|
||||
this.hasLibraries = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public FileCollection getClasspath() {
|
||||
return classpath;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getMainClass() {
|
||||
try {
|
||||
byte[] manifestBytes = ZipUtils.unpackNullable(mainClasspath.toPath(), "META-INF/MANIFEST.MF");
|
||||
|
||||
if (manifestBytes == null) {
|
||||
throw new RuntimeException("Could not find MANIFEST.MF in " + mainClasspath + "!");
|
||||
}
|
||||
|
||||
Manifest manifest = new Manifest(new ByteArrayInputStream(manifestBytes));
|
||||
Attributes attributes = manifest.getMainAttributes();
|
||||
String value = attributes.getValue(Attributes.Name.MAIN_CLASS);
|
||||
|
||||
if (value == null) {
|
||||
throw new RuntimeException("Could not find main class in " + mainClasspath + "!");
|
||||
} else {
|
||||
return value;
|
||||
}
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<String> getArgs(Path input, Path output, Path mappings, FileCollection libraries) {
|
||||
List<String> args = this.args.stream()
|
||||
.map(str -> {
|
||||
return switch (str) {
|
||||
case "{input}" -> input.toAbsolutePath().toString();
|
||||
case "{output}" -> output.toAbsolutePath().toString();
|
||||
case "{mappings}" -> mappings.toAbsolutePath().toString();
|
||||
default -> str;
|
||||
};
|
||||
})
|
||||
.collect(Collectors.toList());
|
||||
|
||||
if (hasLibraries) {
|
||||
for (File file : libraries) {
|
||||
args.add("-e=" + file.getAbsolutePath());
|
||||
}
|
||||
}
|
||||
|
||||
return args;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return this.name;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -29,7 +29,6 @@ import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.io.PrintStream;
|
||||
import java.io.UncheckedIOException;
|
||||
import java.net.URI;
|
||||
import java.nio.file.FileSystem;
|
||||
@@ -42,10 +41,9 @@ import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.Objects;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.function.BiFunction;
|
||||
import java.util.function.Function;
|
||||
import java.util.function.Predicate;
|
||||
import java.util.jar.Attributes;
|
||||
@@ -62,8 +60,6 @@ import dev.architectury.tinyremapper.InputTag;
|
||||
import dev.architectury.tinyremapper.NonClassCopyMode;
|
||||
import dev.architectury.tinyremapper.OutputConsumerPath;
|
||||
import dev.architectury.tinyremapper.TinyRemapper;
|
||||
import net.minecraftforge.binarypatcher.ConsoleTool;
|
||||
import org.apache.commons.io.output.NullOutputStream;
|
||||
import org.gradle.api.Project;
|
||||
import org.gradle.api.logging.LogLevel;
|
||||
import org.gradle.api.logging.Logger;
|
||||
@@ -76,175 +72,156 @@ import org.objectweb.asm.Opcodes;
|
||||
import org.objectweb.asm.tree.ClassNode;
|
||||
|
||||
import net.fabricmc.loom.LoomGradleExtension;
|
||||
import net.fabricmc.loom.LoomGradlePlugin;
|
||||
import net.fabricmc.loom.configuration.accesstransformer.AccessTransformerJarProcessor;
|
||||
import net.fabricmc.loom.configuration.providers.minecraft.MergedMinecraftProvider;
|
||||
import net.fabricmc.loom.configuration.providers.forge.mcpconfig.McpConfigData;
|
||||
import net.fabricmc.loom.configuration.providers.forge.mcpconfig.McpConfigStep;
|
||||
import net.fabricmc.loom.configuration.providers.forge.mcpconfig.McpExecutor;
|
||||
import net.fabricmc.loom.configuration.providers.forge.minecraft.ForgeMinecraftProvider;
|
||||
import net.fabricmc.loom.configuration.providers.minecraft.MinecraftProvider;
|
||||
import net.fabricmc.loom.util.Constants;
|
||||
import net.fabricmc.loom.util.DependencyDownloader;
|
||||
import net.fabricmc.loom.util.FileSystemUtil;
|
||||
import net.fabricmc.loom.util.ForgeToolExecutor;
|
||||
import net.fabricmc.loom.util.MappingsProviderVerbose;
|
||||
import net.fabricmc.loom.util.ThreadingUtils;
|
||||
import net.fabricmc.loom.util.TinyRemapperHelper;
|
||||
import net.fabricmc.loom.util.ZipUtils;
|
||||
import net.fabricmc.loom.util.function.FsPathConsumer;
|
||||
import net.fabricmc.loom.util.srg.InnerClassRemapper;
|
||||
import net.fabricmc.loom.util.srg.SpecialSourceExecutor;
|
||||
import net.fabricmc.mappingio.tree.MemoryMappingTree;
|
||||
|
||||
public class MinecraftPatchedProvider extends MergedMinecraftProvider {
|
||||
public class MinecraftPatchedProvider {
|
||||
private static final String LOOM_PATCH_VERSION_KEY = "Loom-Patch-Version";
|
||||
private static final String CURRENT_LOOM_PATCH_VERSION = "6";
|
||||
private static final String CURRENT_LOOM_PATCH_VERSION = "7";
|
||||
private static final String NAME_MAPPING_SERVICE_PATH = "/inject/META-INF/services/cpw.mods.modlauncher.api.INameMappingService";
|
||||
|
||||
// Step 1: Remap Minecraft to SRG
|
||||
private File minecraftClientSrgJar;
|
||||
private File minecraftServerSrgJar;
|
||||
private final Project project;
|
||||
private final Logger logger;
|
||||
private final MinecraftProvider minecraftProvider;
|
||||
private final Type type;
|
||||
|
||||
// Step 1: Remap Minecraft to SRG, merge if needed
|
||||
private Path minecraftSrgJar;
|
||||
// Step 2: Binary Patch
|
||||
private File minecraftClientPatchedSrgJar;
|
||||
private File minecraftServerPatchedSrgJar;
|
||||
// Step 3: Merge (global)
|
||||
private File minecraftMergedPatchedSrgJar;
|
||||
// Step 4: Access Transform
|
||||
private File minecraftMergedPatchedSrgAtJar;
|
||||
// Step 5: Remap Patched AT & Forge to Official
|
||||
private File minecraftMergedPatchedJar;
|
||||
private File minecraftClientExtra;
|
||||
private Path minecraftPatchedSrgJar;
|
||||
// Step 3: Access Transform
|
||||
private Path minecraftPatchedSrgAtJar;
|
||||
// Step 4: Remap Patched AT & Forge to official
|
||||
private Path minecraftPatchedJar;
|
||||
private Path minecraftClientExtra;
|
||||
|
||||
private boolean dirty;
|
||||
private boolean serverJarInitialized = false;
|
||||
|
||||
public static MergedMinecraftProvider createMergedMinecraftProvider(Project project) {
|
||||
return LoomGradleExtension.get(project).isForge() ? new MinecraftPatchedProvider(project) : new MergedMinecraftProvider(project);
|
||||
}
|
||||
private boolean dirty = false;
|
||||
|
||||
public static MinecraftPatchedProvider get(Project project) {
|
||||
MinecraftProvider provider = LoomGradleExtension.get(project).getMinecraftProvider();
|
||||
|
||||
if (provider instanceof MinecraftPatchedProvider patched) {
|
||||
return patched;
|
||||
if (provider instanceof ForgeMinecraftProvider patched) {
|
||||
return patched.getPatchedProvider();
|
||||
} else {
|
||||
throw new UnsupportedOperationException("Project " + project.getPath() + " does not use MinecraftPatchedProvider!");
|
||||
}
|
||||
}
|
||||
|
||||
public MinecraftPatchedProvider(Project project) {
|
||||
super(project);
|
||||
public MinecraftPatchedProvider(Project project, MinecraftProvider minecraftProvider, Type type) {
|
||||
this.project = project;
|
||||
this.logger = project.getLogger();
|
||||
this.minecraftProvider = minecraftProvider;
|
||||
this.type = type;
|
||||
}
|
||||
|
||||
private LoomGradleExtension getExtension() {
|
||||
return LoomGradleExtension.get(project);
|
||||
}
|
||||
|
||||
private void initPatchedFiles() {
|
||||
String forgeVersion = getExtension().getForgeProvider().getVersion().getCombined();
|
||||
File forgeWorkingDir = dir("forge/" + forgeVersion);
|
||||
Path forgeWorkingDir = minecraftProvider.dir("forge/" + forgeVersion).toPath();
|
||||
String patchId = "forge-" + forgeVersion + "-";
|
||||
|
||||
setJarPrefix(patchId);
|
||||
minecraftProvider.setJarPrefix(patchId);
|
||||
|
||||
minecraftClientSrgJar = new File(forgeWorkingDir, "minecraft-client-srg.jar");
|
||||
minecraftServerSrgJar = new File(forgeWorkingDir, "minecraft-server-srg.jar");
|
||||
minecraftClientPatchedSrgJar = new File(forgeWorkingDir, "client-srg-patched.jar");
|
||||
minecraftServerPatchedSrgJar = new File(forgeWorkingDir, "server-srg-patched.jar");
|
||||
minecraftMergedPatchedSrgJar = new File(forgeWorkingDir, "merged-srg-patched.jar");
|
||||
minecraftMergedPatchedSrgAtJar = new File(forgeWorkingDir, "merged-srg-at-patched.jar");
|
||||
minecraftMergedPatchedJar = new File(forgeWorkingDir, "merged-patched.jar");
|
||||
minecraftClientExtra = new File(forgeWorkingDir, "forge-client-extra.jar");
|
||||
minecraftSrgJar = forgeWorkingDir.resolve("minecraft-" + type.id + "-srg.jar");
|
||||
minecraftPatchedSrgJar = forgeWorkingDir.resolve("minecraft-" + type.id + "-srg-patched.jar");
|
||||
minecraftPatchedSrgAtJar = forgeWorkingDir.resolve("minecraft-" + type.id + "-srg-at-patched.jar");
|
||||
minecraftPatchedJar = forgeWorkingDir.resolve("minecraft-" + type.id + "-patched.jar");
|
||||
minecraftClientExtra = forgeWorkingDir.resolve("forge-client-extra.jar");
|
||||
}
|
||||
|
||||
private File getEffectiveServerJar() throws IOException {
|
||||
if (getServerBundleMetadata() != null) {
|
||||
if (!serverJarInitialized) {
|
||||
extractBundledServerJar();
|
||||
serverJarInitialized = true;
|
||||
}
|
||||
|
||||
return getMinecraftExtractedServerJar();
|
||||
} else {
|
||||
return getMinecraftServerJar();
|
||||
private void cleanAllCache() throws IOException {
|
||||
for (Path path : getGlobalCaches()) {
|
||||
Files.deleteIfExists(path);
|
||||
}
|
||||
}
|
||||
|
||||
public void cleanAllCache() {
|
||||
for (File file : getGlobalCaches()) {
|
||||
file.delete();
|
||||
}
|
||||
}
|
||||
|
||||
private File[] getGlobalCaches() {
|
||||
File[] files = {
|
||||
minecraftClientSrgJar,
|
||||
minecraftServerSrgJar,
|
||||
minecraftClientPatchedSrgJar,
|
||||
minecraftServerPatchedSrgJar,
|
||||
minecraftMergedPatchedSrgJar,
|
||||
private Path[] getGlobalCaches() {
|
||||
Path[] files = {
|
||||
minecraftSrgJar,
|
||||
minecraftPatchedSrgJar,
|
||||
minecraftPatchedSrgAtJar,
|
||||
minecraftPatchedJar,
|
||||
minecraftClientExtra,
|
||||
minecraftMergedPatchedSrgAtJar,
|
||||
minecraftMergedPatchedJar
|
||||
};
|
||||
|
||||
return files;
|
||||
}
|
||||
|
||||
private void checkCache() throws IOException {
|
||||
if (isRefreshDeps() || Stream.of(getGlobalCaches()).anyMatch(((Predicate<File>) File::exists).negate())
|
||||
|| !isPatchedJarUpToDate(minecraftMergedPatchedJar)) {
|
||||
if (LoomGradlePlugin.refreshDeps || Stream.of(getGlobalCaches()).anyMatch(Files::notExists)
|
||||
|| !isPatchedJarUpToDate(minecraftPatchedJar)) {
|
||||
cleanAllCache();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void provide() throws Exception {
|
||||
super.provide();
|
||||
initPatchedFiles();
|
||||
checkCache();
|
||||
|
||||
this.dirty = false;
|
||||
|
||||
if (!minecraftClientSrgJar.exists() || !minecraftServerSrgJar.exists()) {
|
||||
if (Files.notExists(minecraftSrgJar)) {
|
||||
this.dirty = true;
|
||||
// Remap official jars to MCPConfig remapped srg jars
|
||||
createSrgJars(getProject().getLogger());
|
||||
McpConfigData data = getExtension().getMcpConfigProvider().getData();
|
||||
List<McpConfigStep> steps = data.steps().get(type.mcpId);
|
||||
McpExecutor executor = new McpExecutor(project, minecraftProvider, Files.createTempDirectory("loom-mcp"), steps, data.functions());
|
||||
Path output = executor.executeUpTo("rename");
|
||||
Files.copy(output, minecraftSrgJar);
|
||||
}
|
||||
|
||||
if (!minecraftClientPatchedSrgJar.exists() || !minecraftServerPatchedSrgJar.exists()) {
|
||||
if (dirty || Files.notExists(minecraftPatchedSrgJar)) {
|
||||
this.dirty = true;
|
||||
patchJars(getProject().getLogger());
|
||||
patchJars();
|
||||
}
|
||||
|
||||
if (dirty || !minecraftMergedPatchedSrgJar.exists()) {
|
||||
mergeJars(getProject().getLogger());
|
||||
}
|
||||
|
||||
if (!minecraftMergedPatchedSrgAtJar.exists()) {
|
||||
if (dirty || Files.notExists(minecraftPatchedSrgAtJar)) {
|
||||
this.dirty = true;
|
||||
accessTransformForge(getProject().getLogger());
|
||||
accessTransformForge();
|
||||
}
|
||||
}
|
||||
|
||||
public void remapJar() throws Exception {
|
||||
if (dirty) {
|
||||
remapPatchedJar(getProject().getLogger());
|
||||
remapPatchedJar();
|
||||
fillClientExtraJar();
|
||||
}
|
||||
|
||||
this.dirty = false;
|
||||
DependencyProvider.addDependency(getProject(), minecraftClientExtra, Constants.Configurations.FORGE_EXTRA);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void mergeJars() throws IOException {
|
||||
// Don't merge jars in the superclass
|
||||
DependencyProvider.addDependency(project, minecraftClientExtra, Constants.Configurations.FORGE_EXTRA);
|
||||
}
|
||||
|
||||
private void fillClientExtraJar() throws IOException {
|
||||
Files.deleteIfExists(minecraftClientExtra.toPath());
|
||||
Files.deleteIfExists(minecraftClientExtra);
|
||||
FileSystemUtil.getJarFileSystem(minecraftClientExtra, true).close();
|
||||
|
||||
copyNonClassFiles(getMinecraftClientJar(), minecraftClientExtra);
|
||||
copyNonClassFiles(minecraftProvider.getMinecraftClientJar().toPath(), minecraftClientExtra);
|
||||
}
|
||||
|
||||
private TinyRemapper buildRemapper(Path input) throws IOException {
|
||||
Path[] libraries = TinyRemapperHelper.getMinecraftDependencies(getProject());
|
||||
Path[] libraries = TinyRemapperHelper.getMinecraftDependencies(project);
|
||||
MemoryMappingTree mappingsWithSrg = getExtension().getMappingsProvider().getMappingsWithSrg();
|
||||
|
||||
TinyRemapper remapper = TinyRemapper.newRemapper()
|
||||
.logger(getProject().getLogger()::lifecycle)
|
||||
.logger(logger::lifecycle)
|
||||
.logUnknownInvokeDynamic(false)
|
||||
.withMappings(TinyRemapperHelper.create(mappingsWithSrg, "srg", "official", true))
|
||||
.withMappings(InnerClassRemapper.of(InnerClassRemapper.readClassNames(input), mappingsWithSrg, "srg", "official"))
|
||||
@@ -252,7 +229,7 @@ public class MinecraftPatchedProvider extends MergedMinecraftProvider {
|
||||
.rebuildSourceFilenames(true)
|
||||
.build();
|
||||
|
||||
if (getProject().getGradle().getStartParameter().getLogLevel().compareTo(LogLevel.LIFECYCLE) < 0) {
|
||||
if (project.getGradle().getStartParameter().getLogLevel().compareTo(LogLevel.LIFECYCLE) < 0) {
|
||||
MappingsProviderVerbose.saveFile(remapper);
|
||||
}
|
||||
|
||||
@@ -261,36 +238,11 @@ public class MinecraftPatchedProvider extends MergedMinecraftProvider {
|
||||
return remapper;
|
||||
}
|
||||
|
||||
private void createSrgJars(Logger logger) throws Exception {
|
||||
produceSrgJar(super.getMinecraftClientJar().toPath(), getEffectiveServerJar().toPath());
|
||||
}
|
||||
|
||||
private void produceSrgJar(Path clientJar, Path serverJar) throws IOException {
|
||||
Path tmpSrg = getToSrgMappings();
|
||||
Set<File> mcLibs = getProject().getConfigurations().getByName(Constants.Configurations.MINECRAFT_DEPENDENCIES).resolve();
|
||||
|
||||
// These can't be threaded because accessing getRemapAction().getMainClass() can cause a situation where
|
||||
// 1. thread A has an FS open
|
||||
// 2. thread B tries to open a new one, but fails
|
||||
// 3. thread A closes its FS
|
||||
// 4. thread B tries to get the already open one => crash
|
||||
Files.copy(SpecialSourceExecutor.produceSrgJar(getExtension().getMcpConfigProvider().getRemapAction(), getProject(), "client", mcLibs, clientJar, tmpSrg), minecraftClientSrgJar.toPath());
|
||||
Files.copy(SpecialSourceExecutor.produceSrgJar(getExtension().getMcpConfigProvider().getRemapAction(), getProject(), "server", mcLibs, serverJar, tmpSrg), minecraftServerSrgJar.toPath());
|
||||
}
|
||||
|
||||
private Path getToSrgMappings() throws IOException {
|
||||
if (getExtension().getSrgProvider().isTsrgV2()) {
|
||||
return getExtension().getSrgProvider().getMergedMojangRaw();
|
||||
} else {
|
||||
return getExtension().getMcpConfigProvider().getMappings();
|
||||
}
|
||||
}
|
||||
|
||||
private void fixParameterAnnotation(File jarFile) throws Exception {
|
||||
getProject().getLogger().info(":fixing parameter annotations for " + jarFile.getAbsolutePath());
|
||||
private void fixParameterAnnotation(Path jarFile) throws Exception {
|
||||
logger.info(":fixing parameter annotations for " + jarFile.toAbsolutePath());
|
||||
Stopwatch stopwatch = Stopwatch.createStarted();
|
||||
|
||||
try (FileSystem fs = FileSystems.newFileSystem(new URI("jar:" + jarFile.toURI()), ImmutableMap.of("create", false))) {
|
||||
try (FileSystem fs = FileSystems.newFileSystem(new URI("jar:" + jarFile.toUri()), ImmutableMap.of("create", false))) {
|
||||
ThreadingUtils.TaskCompleter completer = ThreadingUtils.taskCompleter();
|
||||
|
||||
for (Path file : (Iterable<? extends Path>) Files.walk(fs.getPath("/"))::iterator) {
|
||||
@@ -317,14 +269,14 @@ public class MinecraftPatchedProvider extends MergedMinecraftProvider {
|
||||
completer.complete();
|
||||
}
|
||||
|
||||
getProject().getLogger().info(":fixing parameter annotations for " + jarFile.getAbsolutePath() + " in " + stopwatch);
|
||||
logger.info(":fixed parameter annotations for " + jarFile.toAbsolutePath() + " in " + stopwatch);
|
||||
}
|
||||
|
||||
private void deleteParameterNames(File jarFile) throws Exception {
|
||||
getProject().getLogger().info(":deleting parameter names for " + jarFile.getAbsolutePath());
|
||||
private void deleteParameterNames(Path jarFile) throws Exception {
|
||||
logger.info(":deleting parameter names for " + jarFile.toAbsolutePath());
|
||||
Stopwatch stopwatch = Stopwatch.createStarted();
|
||||
|
||||
try (FileSystem fs = FileSystems.newFileSystem(new URI("jar:" + jarFile.toURI()), ImmutableMap.of("create", false))) {
|
||||
try (FileSystem fs = FileSystems.newFileSystem(new URI("jar:" + jarFile.toUri()), ImmutableMap.of("create", false))) {
|
||||
ThreadingUtils.TaskCompleter completer = ThreadingUtils.taskCompleter();
|
||||
Pattern vignetteParameters = Pattern.compile("p_[0-9a-zA-Z]+_(?:[0-9a-zA-Z]+_)?");
|
||||
|
||||
@@ -371,7 +323,7 @@ public class MinecraftPatchedProvider extends MergedMinecraftProvider {
|
||||
completer.complete();
|
||||
}
|
||||
|
||||
getProject().getLogger().info(":deleting parameter names for " + jarFile.getAbsolutePath() + " in " + stopwatch);
|
||||
logger.info(":deleted parameter names for " + jarFile.toAbsolutePath() + " in " + stopwatch);
|
||||
}
|
||||
|
||||
private File getForgeJar() {
|
||||
@@ -382,10 +334,10 @@ public class MinecraftPatchedProvider extends MergedMinecraftProvider {
|
||||
return getExtension().getForgeUserdevProvider().getUserdevJar();
|
||||
}
|
||||
|
||||
private boolean isPatchedJarUpToDate(File jar) throws IOException {
|
||||
if (!jar.exists()) return false;
|
||||
private boolean isPatchedJarUpToDate(Path jar) throws IOException {
|
||||
if (Files.notExists(jar)) return false;
|
||||
|
||||
byte[] manifestBytes = ZipUtils.unpackNullable(jar.toPath(), "META-INF/MANIFEST.MF");
|
||||
byte[] manifestBytes = ZipUtils.unpackNullable(jar, "META-INF/MANIFEST.MF");
|
||||
|
||||
if (manifestBytes == null) {
|
||||
return false;
|
||||
@@ -398,68 +350,46 @@ public class MinecraftPatchedProvider extends MergedMinecraftProvider {
|
||||
if (Objects.equals(value, CURRENT_LOOM_PATCH_VERSION)) {
|
||||
return true;
|
||||
} else {
|
||||
getProject().getLogger().lifecycle(":forge patched jars not up to date. current version: " + value);
|
||||
logger.lifecycle(":forge patched jars not up to date. current version: " + value);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private void accessTransformForge(Logger logger) throws Exception {
|
||||
List<File> toDelete = new ArrayList<>();
|
||||
private void accessTransformForge() throws IOException {
|
||||
List<Path> toDelete = new ArrayList<>();
|
||||
Stopwatch stopwatch = Stopwatch.createStarted();
|
||||
|
||||
logger.lifecycle(":access transforming minecraft");
|
||||
|
||||
File input = minecraftMergedPatchedSrgJar;
|
||||
File target = minecraftMergedPatchedSrgAtJar;
|
||||
Files.deleteIfExists(target.toPath());
|
||||
Path input = minecraftPatchedSrgJar;
|
||||
Path target = minecraftPatchedSrgAtJar;
|
||||
Files.deleteIfExists(target);
|
||||
|
||||
AccessTransformerJarProcessor.executeAt(getProject(), input.toPath(), target.toPath(), args -> {
|
||||
for (File jar : ImmutableList.of(getForgeJar(), getForgeUserdevJar(), minecraftMergedPatchedSrgJar)) {
|
||||
byte[] atBytes = ZipUtils.unpackNullable(jar.toPath(), Constants.Forge.ACCESS_TRANSFORMER_PATH);
|
||||
AccessTransformerJarProcessor.executeAt(project, input, target, args -> {
|
||||
for (Path jar : ImmutableList.of(getForgeJar().toPath(), getExtension().getForgeUserdevProvider().getUserdevJar().toPath(), minecraftPatchedSrgJar)) {
|
||||
byte[] atBytes = ZipUtils.unpackNullable(jar, Constants.Forge.ACCESS_TRANSFORMER_PATH);
|
||||
|
||||
if (atBytes != null) {
|
||||
File tmpFile = File.createTempFile("at-conf", ".cfg");
|
||||
Path tmpFile = Files.createTempFile("at-conf", ".cfg");
|
||||
toDelete.add(tmpFile);
|
||||
Files.write(tmpFile.toPath(), atBytes, StandardOpenOption.CREATE, StandardOpenOption.TRUNCATE_EXISTING);
|
||||
Files.write(tmpFile, atBytes, StandardOpenOption.CREATE, StandardOpenOption.TRUNCATE_EXISTING);
|
||||
args.add("--atFile");
|
||||
args.add(tmpFile.getAbsolutePath());
|
||||
args.add(tmpFile.toAbsolutePath().toString());
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
for (File file : toDelete) {
|
||||
file.delete();
|
||||
for (Path file : toDelete) {
|
||||
Files.delete(file);
|
||||
}
|
||||
|
||||
logger.lifecycle(":access transformed minecraft in " + stopwatch.stop());
|
||||
}
|
||||
|
||||
public enum Environment {
|
||||
CLIENT(provider -> provider.minecraftClientSrgJar,
|
||||
provider -> provider.minecraftClientPatchedSrgJar
|
||||
),
|
||||
SERVER(provider -> provider.minecraftServerSrgJar,
|
||||
provider -> provider.minecraftServerPatchedSrgJar
|
||||
);
|
||||
|
||||
final Function<MinecraftPatchedProvider, File> srgJar;
|
||||
final Function<MinecraftPatchedProvider, File> patchedSrgJar;
|
||||
|
||||
Environment(Function<MinecraftPatchedProvider, File> srgJar,
|
||||
Function<MinecraftPatchedProvider, File> patchedSrgJar) {
|
||||
this.srgJar = srgJar;
|
||||
this.patchedSrgJar = patchedSrgJar;
|
||||
}
|
||||
|
||||
public String side() {
|
||||
return name().toLowerCase(Locale.ROOT);
|
||||
}
|
||||
}
|
||||
|
||||
private void remapPatchedJar(Logger logger) throws Exception {
|
||||
getProject().getLogger().lifecycle(":remapping minecraft (TinyRemapper, srg -> official)");
|
||||
Path mcInput = minecraftMergedPatchedSrgAtJar.toPath();
|
||||
Path mcOutput = minecraftMergedPatchedJar.toPath();
|
||||
private void remapPatchedJar() throws Exception {
|
||||
logger.lifecycle(":remapping minecraft (TinyRemapper, srg -> official)");
|
||||
Path mcInput = minecraftPatchedSrgAtJar;
|
||||
Path mcOutput = minecraftPatchedJar;
|
||||
Path forgeJar = getForgeJar().toPath();
|
||||
Path forgeUserdevJar = getForgeUserdevJar().toPath();
|
||||
Files.deleteIfExists(mcOutput);
|
||||
@@ -482,59 +412,44 @@ public class MinecraftPatchedProvider extends MergedMinecraftProvider {
|
||||
remapper.finish();
|
||||
}
|
||||
|
||||
copyUserdevFiles(forgeUserdevJar.toFile(), minecraftMergedPatchedJar);
|
||||
copyUserdevFiles(forgeUserdevJar, minecraftPatchedSrgJar);
|
||||
applyLoomPatchVersion(mcOutput);
|
||||
}
|
||||
|
||||
private void patchJars(Logger logger) throws IOException {
|
||||
private void patchJars() throws Exception {
|
||||
Stopwatch stopwatch = Stopwatch.createStarted();
|
||||
logger.lifecycle(":patching jars");
|
||||
patchJars(minecraftSrgJar, minecraftPatchedSrgJar, type.patches.apply(getExtension().getPatchProvider(), getExtension().getForgeUserdevProvider()));
|
||||
|
||||
PatchProvider patchProvider = getExtension().getPatchProvider();
|
||||
patchJars(minecraftClientSrgJar, minecraftClientPatchedSrgJar, patchProvider.clientPatches);
|
||||
patchJars(minecraftServerSrgJar, minecraftServerPatchedSrgJar, patchProvider.serverPatches);
|
||||
copyMissingClasses(minecraftSrgJar, minecraftPatchedSrgJar);
|
||||
deleteParameterNames(minecraftPatchedSrgJar);
|
||||
|
||||
ThreadingUtils.run(MinecraftPatchedProvider.Environment.values(), environment -> {
|
||||
copyMissingClasses(environment.srgJar.apply(this), environment.patchedSrgJar.apply(this));
|
||||
deleteParameterNames(environment.patchedSrgJar.apply(this));
|
||||
|
||||
if (getExtension().isForgeAndNotOfficial()) {
|
||||
fixParameterAnnotation(environment.patchedSrgJar.apply(this));
|
||||
}
|
||||
});
|
||||
if (getExtension().isForgeAndNotOfficial()) {
|
||||
fixParameterAnnotation(minecraftPatchedSrgJar);
|
||||
}
|
||||
|
||||
logger.lifecycle(":patched jars in " + stopwatch.stop());
|
||||
}
|
||||
|
||||
private void patchJars(File clean, File output, Path patches) throws IOException {
|
||||
PrintStream previous = System.out;
|
||||
private void patchJars(Path clean, Path output, Path patches) {
|
||||
ForgeToolExecutor.exec(project, spec -> {
|
||||
ForgeUserdevProvider.BinaryPatcherConfig config = getExtension().getForgeUserdevProvider().binaryPatcherConfig;
|
||||
spec.classpath(DependencyDownloader.download(project, config.dependency()));
|
||||
spec.getMainClass().set("net.minecraftforge.binarypatcher.ConsoleTool");
|
||||
|
||||
try {
|
||||
System.setOut(new PrintStream(NullOutputStream.NULL_OUTPUT_STREAM));
|
||||
} catch (SecurityException ignored) {
|
||||
// Failed to replace logger filter, just ignore
|
||||
}
|
||||
|
||||
ConsoleTool.main(new String[] {
|
||||
"--clean", clean.getAbsolutePath(),
|
||||
"--output", output.getAbsolutePath(),
|
||||
"--apply", patches.toAbsolutePath().toString()
|
||||
for (String arg : config.args()) {
|
||||
String actual = switch (arg) {
|
||||
case "{clean}" -> clean.toAbsolutePath().toString();
|
||||
case "{output}" -> output.toAbsolutePath().toString();
|
||||
case "{patch}" -> patches.toAbsolutePath().toString();
|
||||
default -> arg;
|
||||
};
|
||||
spec.args(actual);
|
||||
}
|
||||
});
|
||||
|
||||
try {
|
||||
System.setOut(previous);
|
||||
} catch (SecurityException ignored) {
|
||||
// Failed to replace logger filter, just ignore
|
||||
}
|
||||
}
|
||||
|
||||
private void mergeJars(Logger logger) throws IOException {
|
||||
// FIXME: Hack here: There are no server-only classes so we can just copy the client JAR.
|
||||
// This will change if upstream Loom adds the possibility for separate projects/source sets per environment.
|
||||
Files.copy(minecraftClientPatchedSrgJar.toPath(), minecraftMergedPatchedSrgJar.toPath());
|
||||
}
|
||||
|
||||
private void walkFileSystems(File source, File target, Predicate<Path> filter, Function<FileSystem, Iterable<Path>> toWalk, FsPathConsumer action)
|
||||
private void walkFileSystems(Path source, Path target, Predicate<Path> filter, Function<FileSystem, Iterable<Path>> toWalk, FsPathConsumer action)
|
||||
throws IOException {
|
||||
try (FileSystemUtil.Delegate sourceFs = FileSystemUtil.getJarFileSystem(source, false);
|
||||
FileSystemUtil.Delegate targetFs = FileSystemUtil.getJarFileSystem(target, false)) {
|
||||
@@ -559,11 +474,11 @@ public class MinecraftPatchedProvider extends MergedMinecraftProvider {
|
||||
}
|
||||
}
|
||||
|
||||
private void walkFileSystems(File source, File target, Predicate<Path> filter, FsPathConsumer action) throws IOException {
|
||||
private void walkFileSystems(Path source, Path target, Predicate<Path> filter, FsPathConsumer action) throws IOException {
|
||||
walkFileSystems(source, target, filter, FileSystem::getRootDirectories, action);
|
||||
}
|
||||
|
||||
private void copyMissingClasses(File source, File target) throws IOException {
|
||||
private void copyMissingClasses(Path source, Path target) throws IOException {
|
||||
walkFileSystems(source, target, it -> it.toString().endsWith(".class"), (sourceFs, targetFs, sourcePath, targetPath) -> {
|
||||
if (Files.exists(targetPath)) return;
|
||||
Path parent = targetPath.getParent();
|
||||
@@ -576,7 +491,7 @@ public class MinecraftPatchedProvider extends MergedMinecraftProvider {
|
||||
});
|
||||
}
|
||||
|
||||
private void copyNonClassFiles(File source, File target) throws IOException {
|
||||
private void copyNonClassFiles(Path source, Path target) throws IOException {
|
||||
Predicate<Path> filter = file -> {
|
||||
String s = file.toString();
|
||||
return !s.endsWith(".class") && !s.startsWith("/META-INF");
|
||||
@@ -595,7 +510,7 @@ public class MinecraftPatchedProvider extends MergedMinecraftProvider {
|
||||
Files.copy(sourcePath, targetPath, StandardCopyOption.REPLACE_EXISTING);
|
||||
}
|
||||
|
||||
private void copyUserdevFiles(File source, File target) throws IOException {
|
||||
private void copyUserdevFiles(Path source, Path target) throws IOException {
|
||||
// Removes the Forge name mapping service definition so that our own is used.
|
||||
// If there are multiple name mapping services with the same "understanding" pair
|
||||
// (source -> target namespace pair), modlauncher throws a fit and will crash.
|
||||
@@ -633,13 +548,27 @@ public class MinecraftPatchedProvider extends MergedMinecraftProvider {
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Path getMergedJar() {
|
||||
return minecraftMergedPatchedJar.toPath();
|
||||
public Path getMinecraftPatchedSrgJar() {
|
||||
return minecraftPatchedSrgJar;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Path> getMinecraftJars() {
|
||||
return List.of(minecraftMergedPatchedJar.toPath());
|
||||
public Path getMinecraftPatchedJar() {
|
||||
return minecraftPatchedJar;
|
||||
}
|
||||
|
||||
public enum Type {
|
||||
CLIENT_ONLY("client", "client", (patch, userdev) -> patch.clientPatches),
|
||||
SERVER_ONLY("server", "server", (patch, userdev) -> patch.serverPatches),
|
||||
MERGED("merged", "joined", (patch, userdev) -> userdev.joinedPatches);
|
||||
|
||||
private final String id;
|
||||
private final String mcpId;
|
||||
private final BiFunction<PatchProvider, ForgeUserdevProvider, Path> patches;
|
||||
|
||||
Type(String id, String mcpId, BiFunction<PatchProvider, ForgeUserdevProvider, Path> patches) {
|
||||
this.id = id;
|
||||
this.mcpId = mcpId;
|
||||
this.patches = patches;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,66 @@
|
||||
/*
|
||||
* 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.configuration.providers.forge.mcpconfig;
|
||||
|
||||
import java.util.function.Function;
|
||||
|
||||
/**
|
||||
* A string or a variable in an MCPConfig step or function.
|
||||
*
|
||||
* <p>The special config value variable {@value #OUTPUT} is treated
|
||||
* as the current step's output path.
|
||||
*
|
||||
* <p>The suffix {@value #PREVIOUS_OUTPUT_SUFFIX} can be used to suffix step names
|
||||
* to get their output paths.
|
||||
*/
|
||||
public sealed interface ConfigValue {
|
||||
String OUTPUT = "output";
|
||||
String PREVIOUS_OUTPUT_SUFFIX = "Output";
|
||||
String SRG_MAPPINGS_NAME = "mappings";
|
||||
|
||||
<R> R fold(Function<? super Constant, ? extends R> constant, Function<? super Variable, ? extends R> variable);
|
||||
|
||||
static ConfigValue of(String str) {
|
||||
if (str.startsWith("{") && str.endsWith("}")) {
|
||||
return new Variable(str.substring(1, str.length() - 1));
|
||||
}
|
||||
|
||||
return new Constant(str);
|
||||
}
|
||||
|
||||
record Constant(String value) implements ConfigValue {
|
||||
@Override
|
||||
public <R> R fold(Function<? super Constant, ? extends R> constant, Function<? super Variable, ? extends R> variable) {
|
||||
return constant.apply(this);
|
||||
}
|
||||
}
|
||||
|
||||
record Variable(String name) implements ConfigValue {
|
||||
@Override
|
||||
public <R> R fold(Function<? super Constant, ? extends R> constant, Function<? super Variable, ? extends R> variable) {
|
||||
return variable.apply(this);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,70 @@
|
||||
/*
|
||||
* 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.configuration.providers.forge.mcpconfig;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
import com.google.gson.JsonElement;
|
||||
import com.google.gson.JsonObject;
|
||||
|
||||
/**
|
||||
* Data extracted from the MCPConfig JSON file.
|
||||
*
|
||||
* @param mappingsPath the path to srg mappings inside the MCP zip
|
||||
* @param official the value of the {@code official} property
|
||||
* @param steps the MCP step definitions by environment type
|
||||
* @param functions the MCP function definitions by name
|
||||
*/
|
||||
public record McpConfigData(String mappingsPath, boolean official, Map<String, List<McpConfigStep>> steps, Map<String, McpConfigFunction> functions) {
|
||||
public static McpConfigData fromJson(JsonObject json) {
|
||||
String mappingsPath = json.getAsJsonObject("data").get("mappings").getAsString();
|
||||
boolean official = json.has("official") && json.getAsJsonPrimitive("official").getAsBoolean();
|
||||
|
||||
JsonObject stepsJson = json.getAsJsonObject("steps");
|
||||
ImmutableMap.Builder<String, List<McpConfigStep>> stepsBuilder = ImmutableMap.builder();
|
||||
|
||||
for (String key : stepsJson.keySet()) {
|
||||
ImmutableList.Builder<McpConfigStep> stepListBuilder = ImmutableList.builder();
|
||||
|
||||
for (JsonElement child : stepsJson.getAsJsonArray(key)) {
|
||||
stepListBuilder.add(McpConfigStep.fromJson(child.getAsJsonObject()));
|
||||
}
|
||||
|
||||
stepsBuilder.put(key, stepListBuilder.build());
|
||||
}
|
||||
|
||||
JsonObject functionsJson = json.getAsJsonObject("functions");
|
||||
ImmutableMap.Builder<String, McpConfigFunction> functionsBuilder = ImmutableMap.builder();
|
||||
|
||||
for (String key : functionsJson.keySet()) {
|
||||
functionsBuilder.put(key, McpConfigFunction.fromJson(functionsJson.getAsJsonObject(key)));
|
||||
}
|
||||
|
||||
return new McpConfigData(mappingsPath, official, stepsBuilder.build(), functionsBuilder.build());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,81 @@
|
||||
/*
|
||||
* 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.configuration.providers.forge.mcpconfig;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import com.google.gson.JsonArray;
|
||||
import com.google.gson.JsonObject;
|
||||
|
||||
import net.fabricmc.loom.util.function.CollectionUtil;
|
||||
|
||||
/**
|
||||
* An executable program for {@linkplain McpConfigStep steps}.
|
||||
*
|
||||
* @param version the Gradle-style dependency string of the program
|
||||
* @param args the command-line arguments
|
||||
* @param jvmArgs the JVM arguments
|
||||
* @param repo the Maven repository to download the dependency from
|
||||
*/
|
||||
public record McpConfigFunction(String version, List<ConfigValue> args, List<ConfigValue> jvmArgs, String repo) {
|
||||
private static final String VERSION_KEY = "version";
|
||||
private static final String ARGS_KEY = "args";
|
||||
private static final String JVM_ARGS_KEY = "jvmargs";
|
||||
private static final String REPO_KEY = "repo";
|
||||
|
||||
public String getDownloadUrl() {
|
||||
String[] parts = version.split(":");
|
||||
StringBuilder builder = new StringBuilder();
|
||||
builder.append(repo);
|
||||
// Group:
|
||||
builder.append(parts[0].replace('.', '/')).append('/');
|
||||
// Name:
|
||||
builder.append(parts[1]).append('/');
|
||||
// Version:
|
||||
builder.append(parts[2]).append('/');
|
||||
// Artifact:
|
||||
builder.append(parts[1]).append('-').append(parts[2]);
|
||||
|
||||
// Classifier:
|
||||
if (parts.length >= 4) {
|
||||
builder.append('-').append(parts[3]);
|
||||
}
|
||||
|
||||
builder.append(".jar");
|
||||
return builder.toString();
|
||||
}
|
||||
|
||||
public static McpConfigFunction fromJson(JsonObject json) {
|
||||
String version = json.get(VERSION_KEY).getAsString();
|
||||
List<ConfigValue> args = json.has(ARGS_KEY) ? configValuesFromJson(json.getAsJsonArray(ARGS_KEY)) : List.of();
|
||||
List<ConfigValue> jvmArgs = json.has(JVM_ARGS_KEY) ? configValuesFromJson(json.getAsJsonArray(JVM_ARGS_KEY)) : List.of();
|
||||
String repo = json.get(REPO_KEY).getAsString();
|
||||
return new McpConfigFunction(version, args, jvmArgs, repo);
|
||||
}
|
||||
|
||||
private static List<ConfigValue> configValuesFromJson(JsonArray json) {
|
||||
return CollectionUtil.map(json, child -> ConfigValue.of(child.getAsString()));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,116 @@
|
||||
/*
|
||||
* This file is part of fabric-loom, licensed under the MIT License (MIT).
|
||||
*
|
||||
* Copyright (c) 2020-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.configuration.providers.forge.mcpconfig;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.Reader;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.StandardCopyOption;
|
||||
import java.nio.file.StandardOpenOption;
|
||||
|
||||
import com.google.gson.Gson;
|
||||
import com.google.gson.JsonObject;
|
||||
import org.gradle.api.Project;
|
||||
|
||||
import net.fabricmc.loom.configuration.DependencyInfo;
|
||||
import net.fabricmc.loom.configuration.providers.forge.DependencyProvider;
|
||||
import net.fabricmc.loom.util.Constants;
|
||||
import net.fabricmc.loom.util.ZipUtils;
|
||||
|
||||
public class McpConfigProvider extends DependencyProvider {
|
||||
private Path mcp;
|
||||
private Path configJson;
|
||||
private Path mappings;
|
||||
private McpConfigData data;
|
||||
|
||||
public McpConfigProvider(Project project) {
|
||||
super(project);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void provide(DependencyInfo dependency) throws Exception {
|
||||
init(dependency.getDependency().getVersion());
|
||||
|
||||
Path mcpZip = dependency.resolveFile().orElseThrow(() -> new RuntimeException("Could not resolve MCPConfig")).toPath();
|
||||
|
||||
if (!Files.exists(mcp) || !Files.exists(configJson) || isRefreshDeps()) {
|
||||
Files.copy(mcpZip, mcp, StandardCopyOption.REPLACE_EXISTING);
|
||||
Files.write(configJson, ZipUtils.unpack(mcp, "config.json"), StandardOpenOption.CREATE, StandardOpenOption.TRUNCATE_EXISTING);
|
||||
}
|
||||
|
||||
JsonObject json;
|
||||
|
||||
try (Reader reader = Files.newBufferedReader(configJson)) {
|
||||
json = new Gson().fromJson(reader, JsonObject.class);
|
||||
}
|
||||
|
||||
data = McpConfigData.fromJson(json);
|
||||
}
|
||||
|
||||
private void init(String version) throws IOException {
|
||||
Path dir = getMinecraftProvider().dir("mcp/" + version).toPath();
|
||||
mcp = dir.resolve("mcp.zip");
|
||||
configJson = dir.resolve("mcp-config.json");
|
||||
mappings = dir.resolve("mcp-config-mappings.txt");
|
||||
|
||||
if (isRefreshDeps()) {
|
||||
Files.deleteIfExists(mappings);
|
||||
}
|
||||
}
|
||||
|
||||
public Path getMappings() {
|
||||
if (Files.notExists(mappings)) {
|
||||
try {
|
||||
Files.write(mappings, ZipUtils.unpack(getMcp(), getMappingsPath()), StandardOpenOption.CREATE, StandardOpenOption.TRUNCATE_EXISTING);
|
||||
} catch (IOException e) {
|
||||
throw new IllegalStateException("Failed to find mappings '" + getMappingsPath() + "' in " + getMcp() + "!");
|
||||
}
|
||||
}
|
||||
|
||||
return mappings;
|
||||
}
|
||||
|
||||
public Path getMcp() {
|
||||
return mcp;
|
||||
}
|
||||
|
||||
public boolean isOfficial() {
|
||||
return data.official();
|
||||
}
|
||||
|
||||
public String getMappingsPath() {
|
||||
return data.mappingsPath();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getTargetConfig() {
|
||||
return Constants.Configurations.MCP_CONFIG;
|
||||
}
|
||||
|
||||
public McpConfigData getData() {
|
||||
return data;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
/*
|
||||
* 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.configuration.providers.forge.mcpconfig;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
import com.google.gson.JsonObject;
|
||||
|
||||
public record McpConfigStep(String type, String name, Map<String, ConfigValue> config) {
|
||||
private static final String TYPE_KEY = "type";
|
||||
private static final String NAME_KEY = "name";
|
||||
|
||||
public static McpConfigStep fromJson(JsonObject json) {
|
||||
String type = json.get(TYPE_KEY).getAsString();
|
||||
String name = json.has(NAME_KEY) ? json.get(NAME_KEY).getAsString() : type;
|
||||
ImmutableMap.Builder<String, ConfigValue> config = ImmutableMap.builder();
|
||||
|
||||
for (String key : json.keySet()) {
|
||||
if (key.equals(TYPE_KEY) || key.equals(NAME_KEY)) continue;
|
||||
|
||||
config.put(key, ConfigValue.of(json.get(key).getAsString()));
|
||||
}
|
||||
|
||||
return new McpConfigStep(type, name, config.build());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,230 @@
|
||||
/*
|
||||
* 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.configuration.providers.forge.mcpconfig;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.net.HttpURLConnection;
|
||||
import java.net.URL;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import com.google.common.base.Stopwatch;
|
||||
import com.google.common.hash.Hashing;
|
||||
import org.gradle.api.Action;
|
||||
import org.gradle.api.Project;
|
||||
import org.gradle.api.logging.LogLevel;
|
||||
import org.gradle.api.logging.Logger;
|
||||
import org.gradle.process.JavaExecSpec;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import net.fabricmc.loom.LoomGradleExtension;
|
||||
import net.fabricmc.loom.configuration.providers.minecraft.MinecraftProvider;
|
||||
import net.fabricmc.loom.util.Constants;
|
||||
import net.fabricmc.loom.util.ForgeToolExecutor;
|
||||
import net.fabricmc.loom.util.function.CollectionUtil;
|
||||
|
||||
public final class McpExecutor {
|
||||
private static final LogLevel STEP_LOG_LEVEL = LogLevel.LIFECYCLE;
|
||||
private final Project project;
|
||||
private final MinecraftProvider minecraftProvider;
|
||||
private final Path cache;
|
||||
private final List<McpConfigStep> steps;
|
||||
private final Map<String, McpConfigFunction> functions;
|
||||
private final Map<String, String> extraConfig = new HashMap<>();
|
||||
|
||||
public McpExecutor(Project project, MinecraftProvider minecraftProvider, Path cache, List<McpConfigStep> steps, Map<String, McpConfigFunction> functions) {
|
||||
this.project = project;
|
||||
this.minecraftProvider = minecraftProvider;
|
||||
this.cache = cache;
|
||||
this.steps = steps;
|
||||
this.functions = functions;
|
||||
}
|
||||
|
||||
private Path getDownloadCache() throws IOException {
|
||||
Path downloadCache = cache.resolve("downloads");
|
||||
Files.createDirectories(downloadCache);
|
||||
return downloadCache;
|
||||
}
|
||||
|
||||
private Path getStepCache(String step) {
|
||||
return cache.resolve(step);
|
||||
}
|
||||
|
||||
private Path createStepCache(String step) throws IOException {
|
||||
Path stepCache = getStepCache(step);
|
||||
Files.createDirectories(stepCache);
|
||||
return stepCache;
|
||||
}
|
||||
|
||||
private String resolve(McpConfigStep step, ConfigValue value) {
|
||||
return value.fold(ConfigValue.Constant::value, variable -> {
|
||||
String name = variable.name();
|
||||
@Nullable ConfigValue valueFromStep = step.config().get(name);
|
||||
|
||||
// If the variable isn't defined in the step's config map, skip it.
|
||||
// Also skip if it would recurse with the same variable.
|
||||
if (valueFromStep != null && !valueFromStep.equals(variable)) {
|
||||
// Otherwise, resolve the nested variable.
|
||||
return resolve(step, valueFromStep);
|
||||
}
|
||||
|
||||
if (name.equals(ConfigValue.SRG_MAPPINGS_NAME)) {
|
||||
return LoomGradleExtension.get(project).getSrgProvider().getSrg().toAbsolutePath().toString();
|
||||
} else if (extraConfig.containsKey(name)) {
|
||||
return extraConfig.get(name);
|
||||
}
|
||||
|
||||
throw new IllegalArgumentException("Unknown MCP config variable: " + name);
|
||||
});
|
||||
}
|
||||
|
||||
public Path executeUpTo(String step) throws IOException {
|
||||
extraConfig.clear();
|
||||
|
||||
// Find the total number of steps we need to execute.
|
||||
int totalSteps = CollectionUtil.find(steps, s -> s.name().equals(step))
|
||||
.map(s -> steps.indexOf(s) + 1)
|
||||
.orElse(steps.size());
|
||||
int currentStepIndex = 0;
|
||||
|
||||
project.getLogger().log(STEP_LOG_LEVEL, ":executing {} MCP steps", totalSteps);
|
||||
|
||||
for (McpConfigStep currentStep : steps) {
|
||||
currentStepIndex++;
|
||||
StepLogic stepLogic = getStepLogic(currentStep.type());
|
||||
project.getLogger().log(STEP_LOG_LEVEL, ":step {}/{} - {}", currentStepIndex, totalSteps, stepLogic.getDisplayName(currentStep.name()));
|
||||
|
||||
Stopwatch stopwatch = Stopwatch.createStarted();
|
||||
stepLogic.execute(new ExecutionContextImpl(currentStep));
|
||||
project.getLogger().log(STEP_LOG_LEVEL, ":{} done in {}", currentStep.name(), stopwatch.stop());
|
||||
|
||||
if (currentStep.name().equals(step)) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return Path.of(extraConfig.get(ConfigValue.OUTPUT));
|
||||
}
|
||||
|
||||
private StepLogic getStepLogic(String type) {
|
||||
return switch (type) {
|
||||
case "downloadManifest", "downloadJson" -> new StepLogic.NoOp();
|
||||
case "downloadClient" -> new StepLogic.NoOpWithFile(() -> minecraftProvider.getMinecraftClientJar().toPath());
|
||||
case "downloadServer" -> new StepLogic.NoOpWithFile(() -> minecraftProvider.getMinecraftServerJar().toPath());
|
||||
case "strip" -> new StepLogic.Strip();
|
||||
case "listLibraries" -> new StepLogic.ListLibraries();
|
||||
case "downloadClientMappings" -> new StepLogic.DownloadManifestFile(minecraftProvider.getVersionInfo().download("client_mappings"));
|
||||
case "downloadServerMappings" -> new StepLogic.DownloadManifestFile(minecraftProvider.getVersionInfo().download("server_mappings"));
|
||||
default -> {
|
||||
if (functions.containsKey(type)) {
|
||||
yield new StepLogic.OfFunction(functions.get(type));
|
||||
}
|
||||
|
||||
throw new UnsupportedOperationException("MCP config step type: " + type);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
private class ExecutionContextImpl implements StepLogic.ExecutionContext {
|
||||
private final McpConfigStep step;
|
||||
|
||||
ExecutionContextImpl(McpConfigStep step) {
|
||||
this.step = step;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Logger logger() {
|
||||
return project.getLogger();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Path setOutput(String fileName) throws IOException {
|
||||
createStepCache(step.name());
|
||||
return setOutput(getStepCache(step.name()).resolve(fileName));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Path setOutput(Path output) {
|
||||
String absolutePath = output.toAbsolutePath().toString();
|
||||
extraConfig.put(ConfigValue.OUTPUT, absolutePath);
|
||||
extraConfig.put(step.name() + ConfigValue.PREVIOUS_OUTPUT_SUFFIX, absolutePath);
|
||||
return output;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Path mappings() {
|
||||
return LoomGradleExtension.get(project).getMcpConfigProvider().getMappings();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String resolve(ConfigValue value) {
|
||||
return McpExecutor.this.resolve(step, value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Path download(String url) throws IOException {
|
||||
Path path = getDownloadCache().resolve(Hashing.sha256().hashString(url, StandardCharsets.UTF_8).toString().substring(0, 24));
|
||||
redirectAwareDownload(url, path);
|
||||
return path;
|
||||
}
|
||||
|
||||
// Some of these files linked to the old Forge maven, let's follow the redirects to the new one.
|
||||
private static void redirectAwareDownload(String urlString, Path path) throws IOException {
|
||||
URL url = new URL(urlString);
|
||||
|
||||
if (url.getProtocol().equals("http")) {
|
||||
url = new URL("https", url.getHost(), url.getPort(), url.getFile());
|
||||
}
|
||||
|
||||
HttpURLConnection connection = (HttpURLConnection) url.openConnection();
|
||||
connection.connect();
|
||||
|
||||
if (connection.getResponseCode() == HttpURLConnection.HTTP_MOVED_PERM || connection.getResponseCode() == HttpURLConnection.HTTP_MOVED_TEMP) {
|
||||
redirectAwareDownload(connection.getHeaderField("Location"), path);
|
||||
} else {
|
||||
try (InputStream in = connection.getInputStream()) {
|
||||
Files.copy(in, path);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void javaexec(Action<? super JavaExecSpec> configurator) {
|
||||
ForgeToolExecutor.exec(project, configurator).rethrowFailure().assertNormalExitValue();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<File> getMinecraftLibraries() {
|
||||
return project.getConfigurations().getByName(Constants.Configurations.MINECRAFT_DEPENDENCIES).resolve();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,223 @@
|
||||
/*
|
||||
* 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.configuration.providers.forge.mcpconfig;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.io.PrintWriter;
|
||||
import java.net.URL;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.StandardCopyOption;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.function.Supplier;
|
||||
import java.util.jar.Attributes;
|
||||
import java.util.jar.JarFile;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import org.gradle.api.Action;
|
||||
import org.gradle.api.logging.Logger;
|
||||
import org.gradle.process.JavaExecSpec;
|
||||
|
||||
import net.fabricmc.loom.configuration.providers.minecraft.MinecraftVersionMeta;
|
||||
import net.fabricmc.loom.util.FileSystemUtil;
|
||||
import net.fabricmc.loom.util.HashedDownloadUtil;
|
||||
import net.fabricmc.loom.util.ThreadingUtils;
|
||||
import net.fabricmc.loom.util.function.CollectionUtil;
|
||||
|
||||
/**
|
||||
* The logic for executing a step. This corresponds to the {@code type} key in the step JSON format.
|
||||
*/
|
||||
public interface StepLogic {
|
||||
void execute(ExecutionContext context) throws IOException;
|
||||
|
||||
default String getDisplayName(String stepName) {
|
||||
return stepName;
|
||||
}
|
||||
|
||||
interface ExecutionContext {
|
||||
Logger logger();
|
||||
Path setOutput(String fileName) throws IOException;
|
||||
Path setOutput(Path output);
|
||||
/** Mappings extracted from {@code data.mappings} in the MCPConfig JSON. */
|
||||
Path mappings();
|
||||
String resolve(ConfigValue value);
|
||||
Path download(String url) throws IOException;
|
||||
void javaexec(Action<? super JavaExecSpec> configurator);
|
||||
Set<File> getMinecraftLibraries();
|
||||
|
||||
default List<String> resolve(List<ConfigValue> configValues) {
|
||||
return CollectionUtil.map(configValues, this::resolve);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Runs a Forge tool configured by a {@linkplain McpConfigFunction function}.
|
||||
*/
|
||||
final class OfFunction implements StepLogic {
|
||||
private final McpConfigFunction function;
|
||||
|
||||
public OfFunction(McpConfigFunction function) {
|
||||
this.function = function;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void execute(ExecutionContext context) throws IOException {
|
||||
context.setOutput("output");
|
||||
Path jar = context.download(function.getDownloadUrl());
|
||||
String mainClass;
|
||||
|
||||
try (JarFile jarFile = new JarFile(jar.toFile())) {
|
||||
mainClass = jarFile.getManifest().getMainAttributes().getValue(Attributes.Name.MAIN_CLASS);
|
||||
} catch (IOException e) {
|
||||
throw new IOException("Could not determine main class for " + jar.toAbsolutePath(), e);
|
||||
}
|
||||
|
||||
context.javaexec(spec -> {
|
||||
spec.classpath(jar);
|
||||
spec.getMainClass().set(mainClass);
|
||||
spec.args(context.resolve(function.args()));
|
||||
spec.jvmArgs(context.resolve(function.jvmArgs()));
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getDisplayName(String stepName) {
|
||||
return stepName + " with " + function.version();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Strips certain classes from the jar.
|
||||
*/
|
||||
final class Strip implements StepLogic {
|
||||
@Override
|
||||
public void execute(ExecutionContext context) throws IOException {
|
||||
Set<String> filter = Files.readAllLines(context.mappings(), StandardCharsets.UTF_8).stream()
|
||||
.filter(s -> !s.startsWith("\t"))
|
||||
.map(s -> s.split(" ")[0] + ".class")
|
||||
.collect(Collectors.toSet());
|
||||
|
||||
Path input = Path.of(context.resolve(new ConfigValue.Variable("input")));
|
||||
|
||||
try (FileSystemUtil.Delegate output = FileSystemUtil.getJarFileSystem(context.setOutput("stripped.jar"), true)) {
|
||||
try (FileSystemUtil.Delegate fs = FileSystemUtil.getJarFileSystem(input, false)) {
|
||||
ThreadingUtils.TaskCompleter completer = ThreadingUtils.taskCompleter();
|
||||
|
||||
for (Path path : (Iterable<? extends Path>) Files.walk(fs.get().getPath("/"))::iterator) {
|
||||
String trimLeadingSlash = trimLeadingSlash(path.toString());
|
||||
if (!trimLeadingSlash.endsWith(".class")) continue;
|
||||
boolean has = filter.contains(trimLeadingSlash);
|
||||
String s = trimLeadingSlash;
|
||||
|
||||
while (s.contains("$") && !has) {
|
||||
s = s.substring(0, s.lastIndexOf("$")) + ".class";
|
||||
has = filter.contains(s);
|
||||
}
|
||||
|
||||
if (!has) continue;
|
||||
Path to = output.get().getPath(trimLeadingSlash);
|
||||
Path parent = to.getParent();
|
||||
if (parent != null) Files.createDirectories(parent);
|
||||
|
||||
completer.add(() -> {
|
||||
Files.copy(path, to, StandardCopyOption.COPY_ATTRIBUTES);
|
||||
});
|
||||
}
|
||||
|
||||
completer.complete();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static String trimLeadingSlash(String string) {
|
||||
if (string.startsWith(File.separator)) {
|
||||
return string.substring(File.separator.length());
|
||||
} else if (string.startsWith("/")) {
|
||||
return string.substring(1);
|
||||
}
|
||||
|
||||
return string;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Lists the Minecraft libraries into the output file.
|
||||
*/
|
||||
final class ListLibraries implements StepLogic {
|
||||
@Override
|
||||
public void execute(ExecutionContext context) throws IOException {
|
||||
try (PrintWriter writer = new PrintWriter(Files.newBufferedWriter(context.setOutput("libraries.txt")))) {
|
||||
for (File lib : context.getMinecraftLibraries()) {
|
||||
writer.println("-e=" + lib.getAbsolutePath());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Downloads a file from the Minecraft version metadata.
|
||||
*/
|
||||
final class DownloadManifestFile implements StepLogic {
|
||||
private final MinecraftVersionMeta.Download download;
|
||||
|
||||
public DownloadManifestFile(MinecraftVersionMeta.Download download) {
|
||||
this.download = download;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void execute(ExecutionContext context) throws IOException {
|
||||
HashedDownloadUtil.downloadIfInvalid(new URL(download.url()), context.setOutput("output").toFile(), download.sha1(), context.logger(), false);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A no-op step logic that is used for steps automatically executed by Loom earlier.
|
||||
*/
|
||||
final class NoOp implements StepLogic {
|
||||
@Override
|
||||
public void execute(ExecutionContext context) throws IOException {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A no-op step logic that is used for steps automatically executed by Loom earlier.
|
||||
* This one returns a file.
|
||||
*/
|
||||
final class NoOpWithFile implements StepLogic {
|
||||
private final Supplier<Path> path;
|
||||
|
||||
public NoOpWithFile(Supplier<Path> path) {
|
||||
this.path = path;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void execute(ExecutionContext context) throws IOException {
|
||||
context.setOutput(path.get());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
/**
|
||||
* A simple implementation for executing MCPConfig steps.
|
||||
* Doesn't support all steps, just the ones up to {@code rename}
|
||||
* and all custom functions.
|
||||
*/
|
||||
package net.fabricmc.loom.configuration.providers.forge.mcpconfig;
|
||||
@@ -0,0 +1,52 @@
|
||||
/*
|
||||
* 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.configuration.providers.forge.minecraft;
|
||||
|
||||
import org.gradle.api.Project;
|
||||
|
||||
import net.fabricmc.loom.LoomGradleExtension;
|
||||
import net.fabricmc.loom.configuration.providers.forge.MinecraftPatchedProvider;
|
||||
import net.fabricmc.loom.configuration.providers.minecraft.MergedMinecraftProvider;
|
||||
import net.fabricmc.loom.configuration.providers.minecraft.SingleJarMinecraftProvider;
|
||||
|
||||
/**
|
||||
* A {@link net.fabricmc.loom.configuration.providers.minecraft.MinecraftProvider} that
|
||||
* provides a Forge patched Minecraft jar.
|
||||
*/
|
||||
public interface ForgeMinecraftProvider {
|
||||
MinecraftPatchedProvider getPatchedProvider();
|
||||
|
||||
static MergedMinecraftProvider createMerged(Project project) {
|
||||
return LoomGradleExtension.get(project).isForge() ? new MergedForgeMinecraftProvider(project) : new MergedMinecraftProvider(project);
|
||||
}
|
||||
|
||||
static SingleJarMinecraftProvider createServerOnly(Project project) {
|
||||
return LoomGradleExtension.get(project).isForge() ? SingleJarForgeMinecraftProvider.server(project) : SingleJarMinecraftProvider.server(project);
|
||||
}
|
||||
|
||||
static SingleJarMinecraftProvider createClientOnly(Project project) {
|
||||
return LoomGradleExtension.get(project).isForge() ? SingleJarForgeMinecraftProvider.client(project) : SingleJarMinecraftProvider.client(project);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,69 @@
|
||||
/*
|
||||
* 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.configuration.providers.forge.minecraft;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Path;
|
||||
import java.util.List;
|
||||
|
||||
import org.gradle.api.Project;
|
||||
|
||||
import net.fabricmc.loom.configuration.providers.forge.MinecraftPatchedProvider;
|
||||
import net.fabricmc.loom.configuration.providers.minecraft.MergedMinecraftProvider;
|
||||
|
||||
public final class MergedForgeMinecraftProvider extends MergedMinecraftProvider implements ForgeMinecraftProvider {
|
||||
private final MinecraftPatchedProvider patchedProvider;
|
||||
|
||||
public MergedForgeMinecraftProvider(Project project) {
|
||||
super(project);
|
||||
this.patchedProvider = new MinecraftPatchedProvider(project, this, MinecraftPatchedProvider.Type.MERGED);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void provide() throws Exception {
|
||||
super.provide();
|
||||
patchedProvider.provide();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void mergeJars() throws IOException {
|
||||
// Don't merge jars in the superclass
|
||||
}
|
||||
|
||||
@Override
|
||||
public Path getMergedJar() {
|
||||
return patchedProvider.getMinecraftPatchedJar();
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Path> getMinecraftJars() {
|
||||
return List.of(patchedProvider.getMinecraftPatchedJar());
|
||||
}
|
||||
|
||||
@Override
|
||||
public MinecraftPatchedProvider getPatchedProvider() {
|
||||
return patchedProvider;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,76 @@
|
||||
/*
|
||||
* 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.configuration.providers.forge.minecraft;
|
||||
|
||||
import java.nio.file.Path;
|
||||
import java.util.List;
|
||||
|
||||
import org.gradle.api.Project;
|
||||
|
||||
import net.fabricmc.loom.configuration.providers.forge.MinecraftPatchedProvider;
|
||||
import net.fabricmc.loom.configuration.providers.minecraft.SingleJarMinecraftProvider;
|
||||
|
||||
public final class SingleJarForgeMinecraftProvider extends SingleJarMinecraftProvider implements ForgeMinecraftProvider {
|
||||
private final MinecraftPatchedProvider patchedProvider;
|
||||
|
||||
private SingleJarForgeMinecraftProvider(Project project, SingleJarMinecraftProvider.Environment environment) {
|
||||
super(project, environment);
|
||||
this.patchedProvider = new MinecraftPatchedProvider(project, this, provideServer() ? MinecraftPatchedProvider.Type.SERVER_ONLY : MinecraftPatchedProvider.Type.CLIENT_ONLY);
|
||||
}
|
||||
|
||||
public static SingleJarForgeMinecraftProvider server(Project project) {
|
||||
return new SingleJarForgeMinecraftProvider(project, new Server());
|
||||
}
|
||||
|
||||
public static SingleJarForgeMinecraftProvider client(Project project) {
|
||||
return new SingleJarForgeMinecraftProvider(project, new Client());
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean provideClient() {
|
||||
// the client jar is needed for client-extra which the Forge userdev launch thing always checks for
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void processJar() throws Exception {
|
||||
patchedProvider.provide();
|
||||
}
|
||||
|
||||
@Override
|
||||
public MinecraftPatchedProvider getPatchedProvider() {
|
||||
return patchedProvider;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Path getMinecraftEnvOnlyJar() {
|
||||
return patchedProvider.getMinecraftPatchedJar();
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Path> getMinecraftJars() {
|
||||
return List.of(patchedProvider.getMinecraftPatchedJar());
|
||||
}
|
||||
}
|
||||
@@ -34,7 +34,7 @@ import net.fabricmc.loom.configuration.decompile.DecompileConfiguration;
|
||||
import net.fabricmc.loom.configuration.decompile.SingleJarDecompileConfiguration;
|
||||
import net.fabricmc.loom.configuration.decompile.SplitDecompileConfiguration;
|
||||
import net.fabricmc.loom.configuration.processors.JarProcessorManager;
|
||||
import net.fabricmc.loom.configuration.providers.forge.MinecraftPatchedProvider;
|
||||
import net.fabricmc.loom.configuration.providers.forge.minecraft.ForgeMinecraftProvider;
|
||||
import net.fabricmc.loom.configuration.providers.minecraft.mapped.IntermediaryMinecraftProvider;
|
||||
import net.fabricmc.loom.configuration.providers.minecraft.mapped.MappedMinecraftProvider;
|
||||
import net.fabricmc.loom.configuration.providers.minecraft.mapped.NamedMinecraftProvider;
|
||||
@@ -43,7 +43,7 @@ import net.fabricmc.loom.configuration.providers.minecraft.mapped.SrgMinecraftPr
|
||||
|
||||
public enum MinecraftJarConfiguration {
|
||||
MERGED(
|
||||
MinecraftPatchedProvider::createMergedMinecraftProvider,
|
||||
ForgeMinecraftProvider::createMerged,
|
||||
IntermediaryMinecraftProvider.MergedImpl::new,
|
||||
NamedMinecraftProvider.MergedImpl::new,
|
||||
SrgMinecraftProvider.MergedImpl::new,
|
||||
@@ -52,7 +52,7 @@ public enum MinecraftJarConfiguration {
|
||||
List.of("client", "server")
|
||||
),
|
||||
SERVER_ONLY(
|
||||
SingleJarMinecraftProvider::server,
|
||||
ForgeMinecraftProvider::createServerOnly,
|
||||
IntermediaryMinecraftProvider.SingleJarImpl::server,
|
||||
NamedMinecraftProvider.SingleJarImpl::server,
|
||||
SrgMinecraftProvider.SingleJarImpl::server,
|
||||
@@ -61,7 +61,7 @@ public enum MinecraftJarConfiguration {
|
||||
List.of("server")
|
||||
),
|
||||
CLIENT_ONLY(
|
||||
SingleJarMinecraftProvider::client,
|
||||
ForgeMinecraftProvider::createClientOnly,
|
||||
IntermediaryMinecraftProvider.SingleJarImpl::client,
|
||||
NamedMinecraftProvider.SingleJarImpl::client,
|
||||
SrgMinecraftProvider.SingleJarImpl::client,
|
||||
|
||||
@@ -35,12 +35,12 @@ import org.gradle.api.Project;
|
||||
|
||||
import net.fabricmc.loom.configuration.providers.BundleMetadata;
|
||||
|
||||
public final class SingleJarMinecraftProvider extends MinecraftProvider {
|
||||
public class SingleJarMinecraftProvider extends MinecraftProvider {
|
||||
private final Environment environment;
|
||||
|
||||
private Path minecraftEnvOnlyJar;
|
||||
|
||||
private SingleJarMinecraftProvider(Project project, Environment environment) {
|
||||
protected SingleJarMinecraftProvider(Project project, Environment environment) {
|
||||
super(project);
|
||||
this.environment = environment;
|
||||
}
|
||||
@@ -68,7 +68,10 @@ public final class SingleJarMinecraftProvider extends MinecraftProvider {
|
||||
@Override
|
||||
public void provide() throws Exception {
|
||||
super.provide();
|
||||
processJar();
|
||||
}
|
||||
|
||||
protected void processJar() throws Exception {
|
||||
boolean requiresRefresh = isRefreshDeps() || Files.notExists(minecraftEnvOnlyJar);
|
||||
|
||||
if (!requiresRefresh) {
|
||||
@@ -114,13 +117,13 @@ public final class SingleJarMinecraftProvider extends MinecraftProvider {
|
||||
return minecraftEnvOnlyJar;
|
||||
}
|
||||
|
||||
private interface Environment {
|
||||
protected interface Environment {
|
||||
String name();
|
||||
|
||||
Path getInputJar(SingleJarMinecraftProvider provider) throws Exception;
|
||||
}
|
||||
|
||||
private static final class Server implements Environment {
|
||||
public static final class Server implements Environment {
|
||||
@Override
|
||||
public String name() {
|
||||
return "server";
|
||||
@@ -139,7 +142,7 @@ public final class SingleJarMinecraftProvider extends MinecraftProvider {
|
||||
}
|
||||
}
|
||||
|
||||
private static final class Client implements Environment {
|
||||
public static final class Client implements Environment {
|
||||
@Override
|
||||
public String name() {
|
||||
return "client";
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
* This file is part of fabric-loom, licensed under the MIT License (MIT).
|
||||
*
|
||||
* Copyright (c) 2021 FabricMC
|
||||
* Copyright (c) 2021-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
|
||||
@@ -154,14 +154,14 @@ public class ForgeSourcesRemapper {
|
||||
PrintStream out = System.out;
|
||||
PrintStream err = System.err;
|
||||
|
||||
if (!ForgeToolExecutor.shouldShowVerboseOutput(project)) {
|
||||
if (!ForgeToolExecutor.shouldShowVerboseStderr(project)) {
|
||||
System.setOut(new PrintStream(NullOutputStream.NULL_OUTPUT_STREAM));
|
||||
System.setErr(new PrintStream(NullOutputStream.NULL_OUTPUT_STREAM));
|
||||
}
|
||||
|
||||
remapForgeSourcesInner(project, tmpInput.toPath(), tmpOutput.toPath());
|
||||
|
||||
if (!ForgeToolExecutor.shouldShowVerboseOutput(project)) {
|
||||
if (!ForgeToolExecutor.shouldShowVerboseStderr(project)) {
|
||||
System.setOut(out);
|
||||
System.setErr(err);
|
||||
}
|
||||
|
||||
@@ -129,6 +129,8 @@ public class Constants {
|
||||
public static final String FORGE_RUNTIME = "dev.architectury:architectury-loom-runtime:";
|
||||
public static final String ACCESS_TRANSFORMERS = "net.minecraftforge:accesstransformers:";
|
||||
public static final String UNPROTECT = "io.github.juuxel:unprotect:";
|
||||
// Used to upgrade the ASM version for the AT tool.
|
||||
public static final String ASM = "org.ow2.asm:asm:";
|
||||
|
||||
private Dependencies() {
|
||||
}
|
||||
@@ -147,6 +149,7 @@ public class Constants {
|
||||
public static final String ACCESS_TRANSFORMERS = "3.0.1";
|
||||
public static final String ACCESS_TRANSFORMERS_NEW = "8.0.5";
|
||||
public static final String UNPROTECT = "1.0.0";
|
||||
public static final String ASM = "9.3";
|
||||
|
||||
private Versions() {
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
* This file is part of fabric-loom, licensed under the MIT License (MIT).
|
||||
*
|
||||
* Copyright (c) 2021 FabricMC
|
||||
* Copyright (c) 2021-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
|
||||
@@ -26,6 +26,7 @@ package net.fabricmc.loom.util;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
@@ -36,30 +37,58 @@ import org.gradle.api.artifacts.ModuleDependency;
|
||||
import org.gradle.api.file.FileCollection;
|
||||
|
||||
/**
|
||||
* Simplified dependency downloading.
|
||||
* Simplified but powerful dependency downloading.
|
||||
*
|
||||
* @author Juuz
|
||||
*/
|
||||
public final class DependencyDownloader {
|
||||
private final Project project;
|
||||
private final Set<String> dependencies = new HashSet<>();
|
||||
|
||||
public DependencyDownloader(Project project) {
|
||||
this.project = project;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a dependency to download.
|
||||
*
|
||||
* @param dependencyNotation the dependency notation
|
||||
* @return this downloader
|
||||
*/
|
||||
public DependencyDownloader add(String dependencyNotation) {
|
||||
dependencies.add(dependencyNotation);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolves the dependencies as well as their transitive dependencies into a {@link FileCollection}.
|
||||
*
|
||||
* @return the resolved files
|
||||
*/
|
||||
public FileCollection download() {
|
||||
return download(true, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolves a dependency as well as its transitive dependencies into a {@link FileCollection}.
|
||||
*
|
||||
* @param project the project needing these files
|
||||
* @param dependencyNotation the dependency notation
|
||||
* @param transitive whether to include transitive dependencies
|
||||
* @param resolve whether to eagerly resolve the file collection
|
||||
* @return the resolved files
|
||||
*/
|
||||
public static FileCollection download(Project project, String dependencyNotation) {
|
||||
return download(project, dependencyNotation, true, false);
|
||||
}
|
||||
public FileCollection download(boolean transitive, boolean resolve) {
|
||||
Dependency[] dependencies = this.dependencies.stream()
|
||||
.map(notation -> {
|
||||
Dependency dependency = project.getDependencies().create(notation);
|
||||
|
||||
public static FileCollection download(Project project, String dependencyNotation, boolean transitive, boolean resolve) {
|
||||
Dependency dependency = project.getDependencies().create(dependencyNotation);
|
||||
if (dependency instanceof ModuleDependency md) {
|
||||
md.setTransitive(transitive);
|
||||
}
|
||||
|
||||
if (dependency instanceof ModuleDependency) {
|
||||
((ModuleDependency) dependency).setTransitive(transitive);
|
||||
}
|
||||
return dependency;
|
||||
}).toArray(Dependency[]::new);
|
||||
|
||||
Configuration config = project.getConfigurations().detachedConfiguration(dependency);
|
||||
Configuration config = project.getConfigurations().detachedConfiguration(dependencies);
|
||||
config.setTransitive(transitive);
|
||||
FileCollection files = config.fileCollection(dep -> true);
|
||||
|
||||
@@ -70,6 +99,21 @@ public final class DependencyDownloader {
|
||||
return files;
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolves a dependency as well as its transitive dependencies into a {@link FileCollection}.
|
||||
*
|
||||
* @param project the project needing these files
|
||||
* @param dependencyNotation the dependency notation
|
||||
* @return the resolved files
|
||||
*/
|
||||
public static FileCollection download(Project project, String dependencyNotation) {
|
||||
return new DependencyDownloader(project).add(dependencyNotation).download();
|
||||
}
|
||||
|
||||
public static FileCollection download(Project project, String dependencyNotation, boolean transitive, boolean resolve) {
|
||||
return new DependencyDownloader(project).add(dependencyNotation).download(transitive, resolve);
|
||||
}
|
||||
|
||||
private static List<Dependency> collectDependencies(Configuration configuration) {
|
||||
List<Dependency> dependencies = new ArrayList<>();
|
||||
|
||||
|
||||
@@ -37,11 +37,14 @@ import org.gradle.process.JavaExecSpec;
|
||||
* with suppressed output streams to prevent annoying log spam.
|
||||
*/
|
||||
public final class ForgeToolExecutor {
|
||||
public static boolean shouldShowVerboseOutput(Project project) {
|
||||
// if running with INFO or DEBUG logging or stacktraces visible
|
||||
// (stacktrace so errors printed to standard streams show up)
|
||||
return project.getGradle().getStartParameter().getShowStacktrace() != ShowStacktrace.INTERNAL_EXCEPTIONS
|
||||
|| project.getGradle().getStartParameter().getLogLevel().compareTo(LogLevel.LIFECYCLE) < 0;
|
||||
public static boolean shouldShowVerboseStdout(Project project) {
|
||||
// if running with INFO or DEBUG logging
|
||||
return project.getGradle().getStartParameter().getLogLevel().compareTo(LogLevel.LIFECYCLE) < 0;
|
||||
}
|
||||
|
||||
public static boolean shouldShowVerboseStderr(Project project) {
|
||||
// if stdout is shown or stacktraces are visible so that errors printed to stderr show up
|
||||
return shouldShowVerboseStdout(project) || project.getGradle().getStartParameter().getShowStacktrace() != ShowStacktrace.INTERNAL_EXCEPTIONS;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -55,11 +58,15 @@ public final class ForgeToolExecutor {
|
||||
return project.javaexec(spec -> {
|
||||
configurator.execute(spec);
|
||||
|
||||
if (shouldShowVerboseOutput(project)) {
|
||||
if (shouldShowVerboseStdout(project)) {
|
||||
spec.setStandardOutput(System.out);
|
||||
spec.setErrorOutput(System.err);
|
||||
} else {
|
||||
spec.setStandardOutput(NullOutputStream.NULL_OUTPUT_STREAM);
|
||||
}
|
||||
|
||||
if (shouldShowVerboseStderr(project)) {
|
||||
spec.setErrorOutput(System.err);
|
||||
} else {
|
||||
spec.setErrorOutput(NullOutputStream.NULL_OUTPUT_STREAM);
|
||||
}
|
||||
});
|
||||
|
||||
@@ -1,136 +0,0 @@
|
||||
/*
|
||||
* 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.util.srg;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.StandardCopyOption;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import com.google.common.base.Stopwatch;
|
||||
import org.gradle.api.Project;
|
||||
|
||||
import net.fabricmc.loom.LoomGradleExtension;
|
||||
import net.fabricmc.loom.configuration.providers.forge.McpConfigProvider.RemapAction;
|
||||
import net.fabricmc.loom.util.FileSystemUtil;
|
||||
import net.fabricmc.loom.util.ForgeToolExecutor;
|
||||
import net.fabricmc.loom.util.ThreadingUtils;
|
||||
|
||||
public class SpecialSourceExecutor {
|
||||
private static String trimLeadingSlash(String string) {
|
||||
if (string.startsWith(File.separator)) {
|
||||
return string.substring(File.separator.length());
|
||||
} else if (string.startsWith("/")) {
|
||||
return string.substring(1);
|
||||
}
|
||||
|
||||
return string;
|
||||
}
|
||||
|
||||
public static Path produceSrgJar(RemapAction remapAction, Project project, String side, Set<File> mcLibs, Path officialJar, Path mappings)
|
||||
throws IOException {
|
||||
Set<String> filter = Files.readAllLines(mappings, StandardCharsets.UTF_8).stream()
|
||||
.filter(s -> !s.startsWith("\t"))
|
||||
.map(s -> s.split(" ")[0] + ".class")
|
||||
.collect(Collectors.toSet());
|
||||
LoomGradleExtension extension = LoomGradleExtension.get(project.getProject());
|
||||
Path stripped = extension.getFiles().getProjectBuildCache().toPath().resolve(officialJar.getFileName().toString().substring(0, officialJar.getFileName().toString().length() - 4) + "-filtered.jar");
|
||||
Files.deleteIfExists(stripped);
|
||||
|
||||
Stopwatch stopwatch = Stopwatch.createStarted();
|
||||
|
||||
try (FileSystemUtil.Delegate output = FileSystemUtil.getJarFileSystem(stripped, true)) {
|
||||
try (FileSystemUtil.Delegate fs = FileSystemUtil.getJarFileSystem(officialJar, false)) {
|
||||
ThreadingUtils.TaskCompleter completer = ThreadingUtils.taskCompleter();
|
||||
|
||||
for (Path path : (Iterable<? extends Path>) Files.walk(fs.get().getPath("/"))::iterator) {
|
||||
String trimLeadingSlash = trimLeadingSlash(path.toString());
|
||||
if (!trimLeadingSlash.endsWith(".class")) continue;
|
||||
boolean has = filter.contains(trimLeadingSlash);
|
||||
String s = trimLeadingSlash;
|
||||
|
||||
while (s.contains("$") && !has) {
|
||||
s = s.substring(0, s.lastIndexOf("$")) + ".class";
|
||||
has = filter.contains(s);
|
||||
}
|
||||
|
||||
if (!has) continue;
|
||||
Path to = output.get().getPath(trimLeadingSlash);
|
||||
Path parent = to.getParent();
|
||||
if (parent != null) Files.createDirectories(parent);
|
||||
|
||||
completer.add(() -> {
|
||||
Files.copy(path, to, StandardCopyOption.COPY_ATTRIBUTES);
|
||||
});
|
||||
}
|
||||
|
||||
completer.complete();
|
||||
}
|
||||
} finally {
|
||||
project.getLogger().info("Copied class files in " + stopwatch.stop());
|
||||
}
|
||||
|
||||
Path output = extension.getFiles().getProjectBuildCache().toPath().resolve(officialJar.getFileName().toString().substring(0, officialJar.getFileName().toString().length() - 4) + "-srg-output.jar");
|
||||
Files.deleteIfExists(output);
|
||||
stopwatch = Stopwatch.createStarted();
|
||||
|
||||
List<String> args = remapAction.getArgs(stripped, output, mappings, project.files(mcLibs));
|
||||
|
||||
project.getLogger().lifecycle(":remapping minecraft (" + remapAction + ", " + side + ", official -> mojang)");
|
||||
|
||||
Path workingDir = tmpDir();
|
||||
|
||||
ForgeToolExecutor.exec(project, spec -> {
|
||||
spec.setArgs(args);
|
||||
spec.setClasspath(remapAction.getClasspath());
|
||||
spec.workingDir(workingDir.toFile());
|
||||
spec.getMainClass().set(remapAction.getMainClass());
|
||||
}).rethrowFailure().assertNormalExitValue();
|
||||
|
||||
project.getLogger().lifecycle(":remapped minecraft (" + remapAction + ", " + side + ", official -> mojang) in " + stopwatch.stop());
|
||||
|
||||
Files.deleteIfExists(stripped);
|
||||
|
||||
Path tmp = tmpFile();
|
||||
Files.deleteIfExists(tmp);
|
||||
Files.copy(output, tmp);
|
||||
|
||||
Files.deleteIfExists(output);
|
||||
return tmp;
|
||||
}
|
||||
|
||||
private static Path tmpFile() throws IOException {
|
||||
return Files.createTempFile(null, null);
|
||||
}
|
||||
|
||||
private static Path tmpDir() throws IOException {
|
||||
return Files.createTempDirectory(null);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user