Improve run configs (#318) (#338)

Co-authored-by: ShadewRG <onankman@gmail.com>
Co-authored-by: UndeadCat | Shadew <redgalaxysw@gmail.com>
Co-authored-by: liach <7806504+liach@users.noreply.github.com>
dev/0.11
modmuss50 2021-01-29 18:04:39 +00:00 committed by GitHub
parent 17fb2b4656
commit 6a315be278
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
17 changed files with 502 additions and 110 deletions

View File

@ -106,7 +106,7 @@ license {
checkstyle { checkstyle {
configFile = file('checkstyle.xml') configFile = file('checkstyle.xml')
toolVersion = '8.25' toolVersion = '8.39'
} }
checkstyleMain { checkstyleMain {

View File

@ -41,15 +41,19 @@ import java.util.stream.Collectors;
import com.google.gson.JsonObject; import com.google.gson.JsonObject;
import org.cadixdev.lorenz.MappingSet; import org.cadixdev.lorenz.MappingSet;
import org.cadixdev.mercury.Mercury; import org.cadixdev.mercury.Mercury;
import org.gradle.api.Action;
import org.gradle.api.NamedDomainObjectContainer;
import org.gradle.api.Project; import org.gradle.api.Project;
import org.gradle.api.artifacts.Configuration; import org.gradle.api.artifacts.Configuration;
import org.gradle.api.artifacts.Dependency; import org.gradle.api.artifacts.Dependency;
import org.gradle.api.file.ConfigurableFileCollection; import org.gradle.api.file.ConfigurableFileCollection;
import org.gradle.api.plugins.BasePluginConvention; import org.gradle.api.plugins.BasePluginConvention;
import org.jetbrains.annotations.ApiStatus;
import org.jetbrains.annotations.Nullable; import org.jetbrains.annotations.Nullable;
import net.fabricmc.loom.api.decompilers.LoomDecompiler; import net.fabricmc.loom.api.decompilers.LoomDecompiler;
import net.fabricmc.loom.configuration.LoomDependencyManager; import net.fabricmc.loom.configuration.LoomDependencyManager;
import net.fabricmc.loom.configuration.ide.RunConfigSettings;
import net.fabricmc.loom.configuration.processors.JarProcessor; import net.fabricmc.loom.configuration.processors.JarProcessor;
import net.fabricmc.loom.configuration.processors.JarProcessorManager; import net.fabricmc.loom.configuration.processors.JarProcessorManager;
import net.fabricmc.loom.configuration.providers.MinecraftProvider; import net.fabricmc.loom.configuration.providers.MinecraftProvider;
@ -58,7 +62,6 @@ import net.fabricmc.loom.configuration.providers.mappings.MojangMappingsDependen
import net.fabricmc.loom.configuration.providers.minecraft.MinecraftMappedProvider; import net.fabricmc.loom.configuration.providers.minecraft.MinecraftMappedProvider;
public class LoomGradleExtension { public class LoomGradleExtension {
public String runDir = "run";
public String refmapName; public String refmapName;
public String loaderLaunchMethod; public String loaderLaunchMethod;
public boolean remapMod = true; public boolean remapMod = true;
@ -82,6 +85,8 @@ public class LoomGradleExtension {
private Mercury[] srcMercuryCache = new Mercury[2]; private Mercury[] srcMercuryCache = new Mercury[2];
private Set<File> mixinMappings = Collections.synchronizedSet(new HashSet<>()); private Set<File> mixinMappings = Collections.synchronizedSet(new HashSet<>());
private NamedDomainObjectContainer<RunConfigSettings> runs;
/** /**
* Loom will generate a new genSources task (with a new name, based off of {@link LoomDecompiler#name()}) * Loom will generate a new genSources task (with a new name, based off of {@link LoomDecompiler#name()})
* that uses the specified decompiler instead. * that uses the specified decompiler instead.
@ -117,6 +122,8 @@ public class LoomGradleExtension {
this.project = project; this.project = project;
this.autoGenIDERuns = isRootProject(); this.autoGenIDERuns = isRootProject();
this.unmappedMods = project.files(); this.unmappedMods = project.files();
this.runs = project.container(RunConfigSettings.class,
baseName -> new RunConfigSettings(project, baseName));
} }
/** /**
@ -292,7 +299,7 @@ public class LoomGradleExtension {
@Nullable @Nullable
private Dependency getMixinDependency() { private Dependency getMixinDependency() {
return recurseProjects((p) -> { return recurseProjects(p -> {
List<Configuration> configs = new ArrayList<>(); List<Configuration> configs = new ArrayList<>();
// check compile classpath first // check compile classpath first
Configuration possibleCompileClasspath = p.getConfigurations().findByName("compileClasspath"); Configuration possibleCompileClasspath = p.getConfigurations().findByName("compileClasspath");
@ -309,11 +316,7 @@ public class LoomGradleExtension {
return true; return true;
} }
if (name.equalsIgnoreCase("sponge-mixin") && group.equalsIgnoreCase("net.fabricmc")) { return name.equalsIgnoreCase("sponge-mixin") && group.equalsIgnoreCase("net.fabricmc");
return true;
}
return false;
}); });
}); });
} }
@ -426,4 +429,14 @@ public class LoomGradleExtension {
public List<LoomDecompiler> getDecompilers() { public List<LoomDecompiler> getDecompilers() {
return decompilers; return decompilers;
} }
@ApiStatus.Experimental
public void runs(Action<NamedDomainObjectContainer<RunConfigSettings>> action) {
action.execute(runs);
}
@ApiStatus.Experimental
public NamedDomainObjectContainer<RunConfigSettings> getRuns() {
return runs;
}
} }

View File

@ -24,12 +24,10 @@
package net.fabricmc.loom.configuration.ide; package net.fabricmc.loom.configuration.ide;
import java.io.File;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
import java.nio.charset.StandardCharsets; import java.nio.charset.StandardCharsets;
import java.util.List; import java.util.List;
import java.util.Locale;
import java.util.Map; import java.util.Map;
import com.google.common.base.Strings; import com.google.common.base.Strings;
@ -37,9 +35,9 @@ import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap; import com.google.common.collect.ImmutableMap;
import com.google.gson.JsonElement; import com.google.gson.JsonElement;
import com.google.gson.JsonObject; import com.google.gson.JsonObject;
import org.apache.commons.io.FileUtils;
import org.apache.commons.io.IOUtils; import org.apache.commons.io.IOUtils;
import org.gradle.api.Project; import org.gradle.api.Project;
import org.gradle.api.tasks.SourceSet;
import org.gradle.plugins.ide.eclipse.model.EclipseModel; import org.gradle.plugins.ide.eclipse.model.EclipseModel;
import org.w3c.dom.Document; import org.w3c.dom.Document;
import org.w3c.dom.Element; import org.w3c.dom.Element;
@ -54,6 +52,7 @@ public class RunConfig {
public String eclipseProjectName; public String eclipseProjectName;
public String ideaModuleName; public String ideaModuleName;
public String mainClass; public String mainClass;
public String runDirIdeaUrl;
public String runDir; public String runDir;
public String vmArgs; public String vmArgs;
public String programArgs; public String programArgs;
@ -64,7 +63,7 @@ public class RunConfig {
this.addXml(root, "module", ImmutableMap.of("name", ideaModuleName)); this.addXml(root, "module", ImmutableMap.of("name", ideaModuleName));
this.addXml(root, "option", ImmutableMap.of("name", "MAIN_CLASS_NAME", "value", mainClass)); this.addXml(root, "option", ImmutableMap.of("name", "MAIN_CLASS_NAME", "value", mainClass));
this.addXml(root, "option", ImmutableMap.of("name", "WORKING_DIRECTORY", "value", runDir)); this.addXml(root, "option", ImmutableMap.of("name", "WORKING_DIRECTORY", "value", runDirIdeaUrl));
if (!Strings.isNullOrEmpty(vmArgs)) { if (!Strings.isNullOrEmpty(vmArgs)) {
this.addXml(root, "option", ImmutableMap.of("name", "VM_PARAMETERS", "value", vmArgs)); this.addXml(root, "option", ImmutableMap.of("name", "VM_PARAMETERS", "value", vmArgs));
@ -94,8 +93,8 @@ public class RunConfig {
return e; return e;
} }
private static String getIdeaModuleName(Project project) { private static String getIdeaModuleName(Project project, SourceSet srcs) {
String module = project.getName() + ".main"; String module = project.getName() + "." + srcs.getName();
while ((project = project.getParent()) != null) { while ((project = project.getParent()) != null) {
module = project.getName() + "." + module; module = project.getName() + "." + module;
@ -107,12 +106,11 @@ public class RunConfig {
private static void populate(Project project, LoomGradleExtension extension, RunConfig runConfig, String mode) { private static void populate(Project project, LoomGradleExtension extension, RunConfig runConfig, String mode) {
runConfig.configName += extension.isRootProject() ? "" : " (" + project.getPath() + ")"; runConfig.configName += extension.isRootProject() ? "" : " (" + project.getPath() + ")";
runConfig.eclipseProjectName = project.getExtensions().getByType(EclipseModel.class).getProject().getName(); runConfig.eclipseProjectName = project.getExtensions().getByType(EclipseModel.class).getProject().getName();
runConfig.ideaModuleName = getIdeaModuleName(project);
runConfig.runDir = "file://$PROJECT_DIR$/" + extension.runDir;
runConfig.vmArgs = ""; runConfig.vmArgs = "";
runConfig.programArgs = "";
if ("launchwrapper".equals(extension.getLoaderLaunchMethod())) { if ("launchwrapper".equals(extension.getLoaderLaunchMethod())) {
runConfig.mainClass = "net.minecraft.launchwrapper.Launch"; runConfig.mainClass = "net.minecraft.launchwrapper.Launch"; // TODO What about custom tweakers for run configs?
runConfig.programArgs += "--tweakClass " + ("client".equals(mode) ? Constants.LaunchWrapper.DEFAULT_FABRIC_CLIENT_TWEAKER : Constants.LaunchWrapper.DEFAULT_FABRIC_SERVER_TWEAKER); runConfig.programArgs += "--tweakClass " + ("client".equals(mode) ? Constants.LaunchWrapper.DEFAULT_FABRIC_CLIENT_TWEAKER : Constants.LaunchWrapper.DEFAULT_FABRIC_SERVER_TWEAKER);
} else { } else {
runConfig.mainClass = "net.fabricmc.devlaunchinjector.Main"; runConfig.mainClass = "net.fabricmc.devlaunchinjector.Main";
@ -149,35 +147,74 @@ public class RunConfig {
} }
} }
public static RunConfig clientRunConfig(Project project) { // Turns camelCase/PascalCase into Capital Case
LoomGradleExtension extension = project.getExtensions().getByType(LoomGradleExtension.class); // caseConversionExample -> Case Conversion Example
private static String capitalizeCamelCaseName(String name) {
RunConfig ideaClient = new RunConfig(); if (name.length() == 0) {
ideaClient.configName = "Minecraft Client"; return "";
ideaClient.programArgs = "";
populate(project, extension, ideaClient, "client");
ideaClient.vmArgs += getOSClientJVMArgs();
ideaClient.vmArgs += " -Dfabric.dli.main=" + getMainClass("client", extension);
return ideaClient;
} }
public static RunConfig serverRunConfig(Project project) { return name.substring(0, 1).toUpperCase() + name.substring(1).replaceAll("([^A-Z])([A-Z])", "$1 $2");
LoomGradleExtension extension = project.getExtensions().getByType(LoomGradleExtension.class);
RunConfig ideaServer = new RunConfig();
ideaServer.configName = "Minecraft Server";
ideaServer.programArgs = "nogui ";
populate(project, extension, ideaServer, "server");
ideaServer.vmArgs += " -Dfabric.dli.main=" + getMainClass("server", extension);
return ideaServer;
} }
// This can be removed at somepoint, its not ideal but its the best solution I could thing of public static RunConfig runConfig(Project project, RunConfigSettings settings) {
public static boolean needsUpgrade(File file) throws IOException { LoomGradleExtension extension = project.getExtensions().getByType(LoomGradleExtension.class);
String contents = FileUtils.readFileToString(file, StandardCharsets.UTF_8); String name = settings.getName();
return !(contents.contains("net.fabricmc.devlaunchinjector.Main"));
String configName = settings.getConfigName();
String mode = settings.getMode();
SourceSet sourceSet = settings.getSource(project);
String defaultMain = settings.getDefaultMainClass();
if (defaultMain == null) {
throw new IllegalArgumentException("Run configuration '" + name + "' must specify 'defaultMainClass'");
}
if (configName == null) {
configName = "";
String srcName = sourceSet.getName();
if (!srcName.equals(SourceSet.MAIN_SOURCE_SET_NAME)) {
configName += capitalizeCamelCaseName(srcName) + " ";
}
configName += "Minecraft " + capitalizeCamelCaseName(name);
}
if (mode == null) {
mode = name;
}
String runDir = settings.getRunDir();
if (runDir == null) {
runDir = "run";
}
RunConfig runConfig = new RunConfig();
runConfig.configName = configName;
populate(project, extension, runConfig, mode);
runConfig.ideaModuleName = getIdeaModuleName(project, sourceSet);
runConfig.runDirIdeaUrl = "file://$PROJECT_DIR$/" + runDir;
runConfig.runDir = runDir;
// Custom parameters
for (String progArg : settings.getProgramArgs()) {
runConfig.programArgs += " " + progArg;
}
for (String vmArg : settings.getVmArgs()) {
runConfig.vmArgs += " " + vmArg;
}
runConfig.vmArgs += " -Dfabric.dli.main=" + getMainClass(mode, extension, defaultMain);
// Remove unnecessary leading/trailing whitespaces we might have generated
runConfig.programArgs = runConfig.programArgs.trim();
runConfig.vmArgs = runConfig.vmArgs.trim();
return runConfig;
} }
public String fromDummy(String dummy) throws IOException { public String fromDummy(String dummy) throws IOException {
@ -191,6 +228,7 @@ public class RunConfig {
dummyConfig = dummyConfig.replace("%MAIN_CLASS%", mainClass); dummyConfig = dummyConfig.replace("%MAIN_CLASS%", mainClass);
dummyConfig = dummyConfig.replace("%ECLIPSE_PROJECT%", eclipseProjectName); dummyConfig = dummyConfig.replace("%ECLIPSE_PROJECT%", eclipseProjectName);
dummyConfig = dummyConfig.replace("%IDEA_MODULE%", ideaModuleName); dummyConfig = dummyConfig.replace("%IDEA_MODULE%", ideaModuleName);
dummyConfig = dummyConfig.replace("%RUN_DIRECTORY%", runDir);
dummyConfig = dummyConfig.replace("%PROGRAM_ARGS%", programArgs.replaceAll("\"", "&quot;")); dummyConfig = dummyConfig.replace("%PROGRAM_ARGS%", programArgs.replaceAll("\"", "&quot;"));
dummyConfig = dummyConfig.replace("%VM_ARGS%", vmArgs.replaceAll("\"", "&quot;")); dummyConfig = dummyConfig.replace("%VM_ARGS%", vmArgs.replaceAll("\"", "&quot;"));
@ -205,7 +243,7 @@ public class RunConfig {
return ""; return "";
} }
private static String getMainClass(String side, LoomGradleExtension extension) { private static String getMainClass(String side, LoomGradleExtension extension, String defaultMainClass) {
JsonObject installerJson = extension.getInstallerJson(); JsonObject installerJson = extension.getInstallerJson();
if (installerJson != null && installerJson.has("mainClass")) { if (installerJson != null && installerJson.has("mainClass")) {
@ -231,7 +269,7 @@ public class RunConfig {
return "net.minecraft.launchwrapper.Launch"; return "net.minecraft.launchwrapper.Launch";
} }
return "net.fabricmc.loader.launch.knot.Knot" + side.substring(0, 1).toUpperCase(Locale.ROOT) + side.substring(1).toLowerCase(Locale.ROOT); return defaultMainClass;
} }
private static String encodeEscaped(String s) { private static String encodeEscaped(String s) {

View File

@ -0,0 +1,291 @@
/*
* This file is part of fabric-loom, licensed under the MIT License (MIT).
*
* Copyright (c) 2016, 2017, 2018 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.ide;
import java.io.File;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.function.Function;
import org.gradle.api.Named;
import org.gradle.api.Project;
import org.gradle.api.plugins.JavaPluginConvention;
import org.gradle.api.tasks.SourceSet;
import org.jetbrains.annotations.ApiStatus;
import net.fabricmc.loom.LoomGradleExtension;
import net.fabricmc.loom.util.Constants;
import net.fabricmc.loom.util.OperatingSystem;
/**
* Experimental for now, please make sure to direct any suggests towards the github.
*/
@ApiStatus.Experimental
public final class RunConfigSettings implements Named {
/**
* Arguments for the JVM, such as system properties.
*/
private final List<String> vmArgs = new ArrayList<>();
/**
* Arguments for the program's main class.
*/
private final List<String> programArgs = new ArrayList<>();
/**
* The mode to run, which is the name of the run config in {@code fabric_installer.[method].json}.
*/
private String mode;
/**
* The full name of the run configuration, i.e. 'Minecraft Client'.
*
* <p>By default this is determined from the base name.
*/
private String name;
/**
* The default main class of the run configuration.
*
* <p>This can be overwritten in {@code fabric_installer.[method].json}. Note that this <em>doesn't</em> take
* priority over the main class specified in the Fabric installer configuration.
*/
private String defaultMainClass;
/**
* The source set getter, which obtains the source set from the given project.
*/
private Function<Project, SourceSet> source;
/**
* The run directory for this configuration, relative to the root project directory.
*/
private String runDir;
/**
* The base name of the run configuration, which is the name it is created with, i.e. 'client'
*/
private final String baseName;
private final Project project;
private final LoomGradleExtension extension;
public RunConfigSettings(Project project, String baseName) {
this.baseName = baseName;
this.project = project;
this.extension = project.getExtensions().getByType(LoomGradleExtension.class);
mode(baseName);
source("main");
runDir("run");
}
public Project getProject() {
return project;
}
public LoomGradleExtension getExtension() {
return extension;
}
@Override
public String getName() {
return baseName;
}
public List<String> getVmArgs() {
return vmArgs;
}
public List<String> getProgramArgs() {
return programArgs;
}
public String getMode() {
return mode;
}
public void setMode(String mode) {
this.mode = mode;
}
public String getConfigName() {
return name;
}
public void setConfigName(String name) {
this.name = name;
}
public String getDefaultMainClass() {
return defaultMainClass;
}
public void setDefaultMainClass(String defaultMainClass) {
this.defaultMainClass = defaultMainClass;
}
public String getRunDir() {
return runDir;
}
public void setRunDir(String runDir) {
this.runDir = runDir;
}
public SourceSet getSource(Project proj) {
return source.apply(proj);
}
public void setSource(SourceSet source) {
this.source = proj -> source;
}
public void setSource(Function<Project, SourceSet> sourceFn) {
this.source = sourceFn;
}
public void mode(String mode) {
setMode(mode);
}
public void name(String name) {
setConfigName(name);
}
public void defaultMainClass(String cls) {
setDefaultMainClass(cls);
}
public void runDir(String dir) {
setRunDir(dir);
}
public void vmArg(String arg) {
vmArgs.add(arg);
}
public void vmArgs(String... args) {
vmArgs.addAll(Arrays.asList(args));
}
public void vmArgs(Collection<String> args) {
vmArgs.addAll(args);
}
public void property(String name, String value) {
vmArg("-D" + name + "=" + value);
}
public void property(String name) {
vmArg("-D" + name);
}
public void properties(Map<String, String> props) {
props.forEach(this::property);
}
public void programArg(String arg) {
programArgs.add(arg);
}
public void programArgs(String... args) {
programArgs.addAll(Arrays.asList(args));
}
public void programArgs(Collection<String> args) {
programArgs.addAll(args);
}
public void source(SourceSet source) {
setSource(source);
}
public void source(String source) {
setSource(proj -> {
JavaPluginConvention conv = proj.getConvention().getPlugin(JavaPluginConvention.class);
return conv.getSourceSets().getByName(source);
});
}
/**
* Add the {@code -XstartOnFirstThread} JVM argument when on OSX.
*/
public void startFirstThread() {
if (OperatingSystem.getOS().equalsIgnoreCase("osx")) {
vmArg("-XstartOnFirstThread");
}
}
/**
* Removes the {@code nogui} argument for the server configuration. By default {@code nogui} is specified, this is
* a convenient way to remove it if wanted.
*/
public void serverWithGui() {
programArgs.removeIf("nogui"::equals);
}
/**
* Configure run config with the default client options.
*/
public void client() {
startFirstThread();
mode("client");
defaultMainClass(Constants.Knot.KNOT_CLIENT);
}
/**
* Configure run config with the default server options.
*/
public void server() {
programArg("nogui");
mode("server");
defaultMainClass(Constants.Knot.KNOT_SERVER);
}
/**
* Copies settings from another run configuration.
*/
public void inherit(RunConfigSettings parent) {
vmArgs.addAll(0, parent.vmArgs);
programArgs.addAll(0, parent.programArgs);
mode = parent.mode;
name = parent.name;
defaultMainClass = parent.defaultMainClass;
source = parent.source;
}
public void makeRunDir() {
File file = new File(getProject().getRootDir(), runDir);
if (!file.exists()) {
file.mkdir();
}
}
}

View File

@ -50,12 +50,6 @@ public class SetupIntelijRunConfigs {
} catch (IOException e) { } catch (IOException e) {
throw new RuntimeException("Failed to generate run configs", e); throw new RuntimeException("Failed to generate run configs", e);
} }
File runDir = new File(project.getRootDir(), extension.runDir);
if (!runDir.exists()) {
runDir.mkdirs();
}
} }
private static void generate(Project project) throws IOException { private static void generate(Project project) throws IOException {
@ -72,22 +66,23 @@ public class SetupIntelijRunConfigs {
File projectDir = rootProject.file(".idea"); File projectDir = rootProject.file(".idea");
File runConfigsDir = new File(projectDir, "runConfigurations"); File runConfigsDir = new File(projectDir, "runConfigurations");
File clientRunConfigs = new File(runConfigsDir, "Minecraft_Client" + projectPath + ".xml");
File serverRunConfigs = new File(runConfigsDir, "Minecraft_Server" + projectPath + ".xml");
if (!runConfigsDir.exists()) { if (!runConfigsDir.exists()) {
runConfigsDir.mkdirs(); runConfigsDir.mkdirs();
} }
String clientRunConfig = RunConfig.clientRunConfig(project).fromDummy("idea_run_config_template.xml"); for (RunConfigSettings settings : extension.getRuns()) {
String serverRunConfig = RunConfig.serverRunConfig(project).fromDummy("idea_run_config_template.xml"); RunConfig config = RunConfig.runConfig(project, settings);
String name = config.configName.replaceAll("[^a-zA-Z0-9$_]", "_");
if (!clientRunConfigs.exists() || RunConfig.needsUpgrade(clientRunConfigs)) { File runConfigs = new File(runConfigsDir, name + projectPath + ".xml");
FileUtils.writeStringToFile(clientRunConfigs, clientRunConfig, StandardCharsets.UTF_8); String runConfigXml = config.fromDummy("idea_run_config_template.xml");
if (!runConfigs.exists()) {
FileUtils.writeStringToFile(runConfigs, runConfigXml, StandardCharsets.UTF_8);
} }
if (!serverRunConfigs.exists() || RunConfig.needsUpgrade(serverRunConfigs)) { settings.makeRunDir();
FileUtils.writeStringToFile(serverRunConfigs, serverRunConfig, StandardCharsets.UTF_8);
} }
} }
} }

View File

@ -85,8 +85,7 @@ public abstract class AbstractRunTask extends JavaExec {
} }
args(argsSplit); args(argsSplit);
LoomGradleExtension extension = this.getProject().getExtensions().getByType(LoomGradleExtension.class); setWorkingDir(new File(getProject().getRootDir(), config.runDir));
setWorkingDir(new File(getProject().getRootDir(), extension.runDir));
super.exec(); super.exec();
} }

View File

@ -32,30 +32,28 @@ import org.apache.commons.io.FileUtils;
import org.gradle.api.tasks.TaskAction; import org.gradle.api.tasks.TaskAction;
import org.gradle.plugins.ide.eclipse.model.EclipseModel; import org.gradle.plugins.ide.eclipse.model.EclipseModel;
import net.fabricmc.loom.LoomGradleExtension;
import net.fabricmc.loom.configuration.ide.RunConfig; import net.fabricmc.loom.configuration.ide.RunConfig;
import net.fabricmc.loom.configuration.ide.RunConfigSettings;
public class GenEclipseRunsTask extends AbstractLoomTask { public class GenEclipseRunsTask extends AbstractLoomTask {
@TaskAction @TaskAction
public void genRuns() throws IOException { public void genRuns() throws IOException {
EclipseModel eclipseModel = getProject().getExtensions().getByType(EclipseModel.class); EclipseModel eclipseModel = getProject().getExtensions().getByType(EclipseModel.class);
File clientRunConfigs = new File(getProject().getRootDir(), eclipseModel.getProject().getName() + "_client.launch"); LoomGradleExtension extension = getExtension();
File serverRunConfigs = new File(getProject().getRootDir(), eclipseModel.getProject().getName() + "_server.launch");
String clientRunConfig = RunConfig.clientRunConfig(getProject()).fromDummy("eclipse_run_config_template.xml"); for (RunConfigSettings settings : extension.getRuns()) {
String serverRunConfig = RunConfig.serverRunConfig(getProject()).fromDummy("eclipse_run_config_template.xml"); String name = settings.getName();
if (!clientRunConfigs.exists() || RunConfig.needsUpgrade(clientRunConfigs)) { File configs = new File(getProject().getRootDir(), eclipseModel.getProject().getName() + "_" + name + ".launch");
FileUtils.writeStringToFile(clientRunConfigs, clientRunConfig, StandardCharsets.UTF_8); RunConfig configInst = RunConfig.runConfig(getProject(), settings);
String config = configInst.fromDummy("eclipse_run_config_template.xml");
if (!configs.exists()) {
FileUtils.writeStringToFile(configs, config, StandardCharsets.UTF_8);
} }
if (!serverRunConfigs.exists() || RunConfig.needsUpgrade(serverRunConfigs)) { settings.makeRunDir();
FileUtils.writeStringToFile(serverRunConfigs, serverRunConfig, StandardCharsets.UTF_8);
}
File runDir = new File(getProject().getRootDir(), getExtension().runDir);
if (!runDir.exists()) {
runDir.mkdirs();
} }
} }
} }

View File

@ -46,6 +46,7 @@ import org.xml.sax.SAXException;
import net.fabricmc.loom.LoomGradleExtension; import net.fabricmc.loom.LoomGradleExtension;
import net.fabricmc.loom.configuration.ide.RunConfig; import net.fabricmc.loom.configuration.ide.RunConfig;
import net.fabricmc.loom.configuration.ide.RunConfigSettings;
public class GenIdeaProjectTask extends AbstractLoomTask { public class GenIdeaProjectTask extends AbstractLoomTask {
@TaskAction @TaskAction
@ -82,8 +83,10 @@ public class GenIdeaProjectTask extends AbstractLoomTask {
throw new RuntimeException("Failed to generate IntelliJ run configurations (runManager was not found)"); throw new RuntimeException("Failed to generate IntelliJ run configurations (runManager was not found)");
} }
runManager.appendChild(RunConfig.clientRunConfig(project).genRuns(runManager)); for (RunConfigSettings settings : getExtension().getRuns()) {
runManager.appendChild(RunConfig.serverRunConfig(project).genRuns(runManager)); runManager.appendChild(RunConfig.runConfig(project, settings).genRuns(runManager));
settings.makeRunDir();
}
TransformerFactory transformerFactory = TransformerFactory.newInstance(); TransformerFactory transformerFactory = TransformerFactory.newInstance();
Transformer transformer = transformerFactory.newTransformer(); Transformer transformer = transformerFactory.newTransformer();
@ -92,11 +95,5 @@ public class GenIdeaProjectTask extends AbstractLoomTask {
transformer.setOutputProperty(OutputKeys.INDENT, "yes"); transformer.setOutputProperty(OutputKeys.INDENT, "yes");
transformer.setOutputProperty("{http://xml.apache.org/xslt}indent-amount", "2"); transformer.setOutputProperty("{http://xml.apache.org/xslt}indent-amount", "2");
transformer.transform(source, result); transformer.transform(source, result);
File runDir = new File(getProject().getRootDir(), extension.runDir);
if (!runDir.exists()) {
runDir.mkdirs();
}
} }
} }

View File

@ -38,6 +38,7 @@ import org.gradle.api.tasks.TaskAction;
import net.fabricmc.loom.LoomGradleExtension; import net.fabricmc.loom.LoomGradleExtension;
import net.fabricmc.loom.configuration.ide.RunConfig; import net.fabricmc.loom.configuration.ide.RunConfig;
import net.fabricmc.loom.configuration.ide.RunConfigSettings;
// Recommended vscode plugins: // Recommended vscode plugins:
// https://marketplace.visualstudio.com/items?itemName=redhat.java // https://marketplace.visualstudio.com/items?itemName=redhat.java
@ -61,8 +62,11 @@ public class GenVsCodeProjectTask extends AbstractLoomTask {
} }
VsCodeLaunch launch = new VsCodeLaunch(); VsCodeLaunch launch = new VsCodeLaunch();
launch.add(RunConfig.clientRunConfig(project));
launch.add(RunConfig.serverRunConfig(project)); for (RunConfigSettings settings : getExtension().getRuns()) {
launch.add(RunConfig.runConfig(project, settings));
settings.makeRunDir();
}
Gson gson = new GsonBuilder().setPrettyPrinting().create(); Gson gson = new GsonBuilder().setPrettyPrinting().create();
String json = gson.toJson(launch); String json = gson.toJson(launch);
@ -72,12 +76,6 @@ public class GenVsCodeProjectTask extends AbstractLoomTask {
} catch (IOException e) { } catch (IOException e) {
throw new RuntimeException("Failed to write launch.json", e); throw new RuntimeException("Failed to write launch.json", e);
} }
File runDir = new File(project.getRootDir(), extension.runDir);
if (!runDir.exists()) {
runDir.mkdirs();
}
} }
private static class VsCodeLaunch { private static class VsCodeLaunch {
@ -94,7 +92,7 @@ public class GenVsCodeProjectTask extends AbstractLoomTask {
public String type = "java"; public String type = "java";
public String name; public String name;
public String request = "launch"; public String request = "launch";
public String cwd = "${workspaceFolder}/run"; public String cwd;
public String console = "internalConsole"; public String console = "internalConsole";
public boolean stopOnEntry = false; public boolean stopOnEntry = false;
public String mainClass; public String mainClass;
@ -106,6 +104,7 @@ public class GenVsCodeProjectTask extends AbstractLoomTask {
this.mainClass = runConfig.mainClass; this.mainClass = runConfig.mainClass;
this.vmArgs = runConfig.vmArgs; this.vmArgs = runConfig.vmArgs;
this.args = runConfig.programArgs; this.args = runConfig.programArgs;
this.cwd = "${workspaceFolder}/" + runConfig.runDir;
} }
} }
} }

View File

@ -29,6 +29,7 @@ import org.gradle.api.tasks.TaskContainer;
import net.fabricmc.loom.LoomGradleExtension; import net.fabricmc.loom.LoomGradleExtension;
import net.fabricmc.loom.api.decompilers.LoomDecompiler; import net.fabricmc.loom.api.decompilers.LoomDecompiler;
import net.fabricmc.loom.configuration.ide.RunConfigSettings;
import net.fabricmc.loom.decompilers.fernflower.FabricFernFlowerDecompiler; import net.fabricmc.loom.decompilers.fernflower.FabricFernFlowerDecompiler;
public final class LoomTasks { public final class LoomTasks {
@ -40,7 +41,7 @@ public final class LoomTasks {
tasks.register("migrateMappings", MigrateMappingsTask.class, t -> { tasks.register("migrateMappings", MigrateMappingsTask.class, t -> {
t.setDescription("Migrates mappings to a new version."); t.setDescription("Migrates mappings to a new version.");
t.getOutputs().upToDateWhen((o) -> false); t.getOutputs().upToDateWhen(o -> false);
}); });
tasks.register("remapJar", RemapJarTask.class, t -> { tasks.register("remapJar", RemapJarTask.class, t -> {
@ -52,7 +53,7 @@ public final class LoomTasks {
tasks.register("remapSourcesJar", RemapSourcesJarTask.class, t -> t.setDescription("Remaps the project sources jar to intermediary names.")); tasks.register("remapSourcesJar", RemapSourcesJarTask.class, t -> t.setDescription("Remaps the project sources jar to intermediary names."));
registerIDETasks(tasks); registerIDETasks(tasks);
registerRunTasks(tasks); registerRunTasks(tasks, project);
registerDecompileTasks(tasks, project); registerDecompileTasks(tasks, project);
} }
@ -81,25 +82,31 @@ public final class LoomTasks {
}); });
} }
private static void registerRunTasks(TaskContainer tasks) { private static void registerRunTasks(TaskContainer tasks, Project project) {
tasks.register("runClient", RunClientTask.class, t -> { LoomGradleExtension extension = project.getExtensions().getByType(LoomGradleExtension.class);
t.setDescription("Starts a development version of the Minecraft client.");
t.dependsOn("downloadAssets"); extension.getRuns().create("client", RunConfigSettings::client);
extension.getRuns().create("server", RunConfigSettings::server);
project.afterEvaluate(p -> {
for (RunConfigSettings config : extension.getRuns()) {
String configName = config.getName();
String taskName = "run" + configName.substring(0, 1).toUpperCase() + configName.substring(1);
tasks.register(taskName, RunGameTask.class, config).configure(t -> {
t.setDescription("Starts the '" + config.getConfigName() + "' run configuration");
t.setGroup("fabric"); t.setGroup("fabric");
}); });
}
tasks.register("runServer", RunServerTask.class, t -> {
t.setDescription("Starts a development version of the Minecraft server.");
t.setGroup("fabric");
}); });
} }
private static void registerDecompileTasks(TaskContainer tasks, Project project) { private static void registerDecompileTasks(TaskContainer tasks, Project project) {
LoomGradleExtension extension = project.getExtensions().getByType(LoomGradleExtension.class); LoomGradleExtension extension = project.getExtensions().getByType(LoomGradleExtension.class);
project.afterEvaluate((p) -> { project.afterEvaluate(p -> {
for (LoomDecompiler decompiler : extension.getDecompilers()) { for (LoomDecompiler decompiler : extension.getDecompilers()) {
String taskName = (decompiler instanceof FabricFernFlowerDecompiler) ? "genSources" : "genSourcesWith" + decompiler.name(); String taskName = decompiler instanceof FabricFernFlowerDecompiler ? "genSources" : "genSourcesWith" + decompiler.name();
// decompiler will be passed to the constructor of GenerateSourcesTask // decompiler will be passed to the constructor of GenerateSourcesTask
tasks.register(taskName, GenerateSourcesTask.class, decompiler); tasks.register(taskName, GenerateSourcesTask.class, decompiler);
} }

View File

@ -24,10 +24,15 @@
package net.fabricmc.loom.task; package net.fabricmc.loom.task;
import net.fabricmc.loom.LoomGradleExtension;
import net.fabricmc.loom.configuration.ide.RunConfig; import net.fabricmc.loom.configuration.ide.RunConfig;
@Deprecated // Replaced by RunGameTask
public class RunClientTask extends AbstractRunTask { public class RunClientTask extends AbstractRunTask {
public RunClientTask() { public RunClientTask() {
super(RunConfig::clientRunConfig); super(project -> {
LoomGradleExtension extension = project.getExtensions().getByType(LoomGradleExtension.class);
return RunConfig.runConfig(project, extension.getRuns().getByName("client"));
});
} }
} }

View File

@ -0,0 +1,37 @@
/*
* This file is part of fabric-loom, licensed under the MIT License (MIT).
*
* Copyright (c) 2016, 2017, 2018 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.task;
import javax.inject.Inject;
import net.fabricmc.loom.configuration.ide.RunConfig;
import net.fabricmc.loom.configuration.ide.RunConfigSettings;
public class RunGameTask extends AbstractRunTask {
@Inject
public RunGameTask(RunConfigSettings settings) {
super(proj -> RunConfig.runConfig(proj, settings));
}
}

View File

@ -24,10 +24,15 @@
package net.fabricmc.loom.task; package net.fabricmc.loom.task;
import net.fabricmc.loom.LoomGradleExtension;
import net.fabricmc.loom.configuration.ide.RunConfig; import net.fabricmc.loom.configuration.ide.RunConfig;
@Deprecated // Replaced by RunGameTask
public class RunServerTask extends AbstractRunTask { public class RunServerTask extends AbstractRunTask {
public RunServerTask() { public RunServerTask() {
super(RunConfig::serverRunConfig); super(project -> {
LoomGradleExtension extension = project.getExtensions().getByType(LoomGradleExtension.class);
return RunConfig.runConfig(project, extension.getRuns().getByName("client"));
});
} }
} }

View File

@ -126,4 +126,12 @@ public class Constants {
private LaunchWrapper() { private LaunchWrapper() {
} }
} }
public static final class Knot {
public static final String KNOT_CLIENT = "net.fabricmc.loader.launch.knot.KnotClient";
public static final String KNOT_SERVER = "net.fabricmc.loader.launch.knot.KnotServer";
private Knot() {
}
}
} }

View File

@ -12,5 +12,5 @@
<stringAttribute key="org.eclipse.jdt.launching.PROGRAM_ARGUMENTS" value="%PROGRAM_ARGS%"/> <stringAttribute key="org.eclipse.jdt.launching.PROGRAM_ARGUMENTS" value="%PROGRAM_ARGS%"/>
<stringAttribute key="org.eclipse.jdt.launching.PROJECT_ATTR" value="%ECLIPSE_PROJECT%"/> <stringAttribute key="org.eclipse.jdt.launching.PROJECT_ATTR" value="%ECLIPSE_PROJECT%"/>
<stringAttribute key="org.eclipse.jdt.launching.VM_ARGUMENTS" value="%VM_ARGS%"/> <stringAttribute key="org.eclipse.jdt.launching.VM_ARGUMENTS" value="%VM_ARGS%"/>
<stringAttribute key="org.eclipse.jdt.launching.WORKING_DIRECTORY" value="${workspace_loc:%ECLIPSE_PROJECT%}/run"/> <stringAttribute key="org.eclipse.jdt.launching.WORKING_DIRECTORY" value="${workspace_loc:%ECLIPSE_PROJECT%}/%RUN_DIRECTORY%"/>
</launchConfiguration> </launchConfiguration>

View File

@ -4,7 +4,7 @@
<module name="%IDEA_MODULE%" /> <module name="%IDEA_MODULE%" />
<option name="PROGRAM_PARAMETERS" value="%PROGRAM_ARGS%" /> <option name="PROGRAM_PARAMETERS" value="%PROGRAM_ARGS%" />
<option name="VM_PARAMETERS" value="%VM_ARGS%" /> <option name="VM_PARAMETERS" value="%VM_ARGS%" />
<option name="WORKING_DIRECTORY" value="$PROJECT_DIR$/run/" /> <option name="WORKING_DIRECTORY" value="$PROJECT_DIR$/%RUN_DIRECTORY%/" />
<method v="2"> <method v="2">
<option name="Make" enabled="true" /> <option name="Make" enabled="true" />
</method> </method>

View File

@ -54,7 +54,7 @@ class MixinBuildFunctionalTest extends Specification {
when: when:
def result = GradleRunner.create() def result = GradleRunner.create()
.withProjectDir(testProjectDir.root) .withProjectDir(testProjectDir.root)
.withArguments('build') .withArguments('build','--stacktrace')
.withPluginClasspath() .withPluginClasspath()
.forwardOutput() .forwardOutput()
.build() .build()