From 8b6658c55966614a040b17e64f0581bdce7fbd7b Mon Sep 17 00:00:00 2001 From: modmuss Date: Thu, 2 Jan 2025 14:25:32 +0000 Subject: [PATCH] Production run tasks (#1241) * Production run tasks * Fix * Add some docs --- gradle/runtime.libs.versions.toml | 4 +- gradle/test.libs.versions.toml | 4 +- .../configuration/LoomConfigurations.java | 5 + .../task/prod/AbstractProductionRunTask.java | 203 ++++++++++++++++++ .../task/prod/ClientProductionRunTask.java | 93 ++++++++ .../task/prod/ServerProductionRunTask.java | 108 ++++++++++ .../net/fabricmc/loom/util/Constants.java | 4 + .../test/integration/RunConfigTest.groovy | 52 ++++- .../loom/test/util/ServerRunner.groovy | 3 +- 9 files changed, 470 insertions(+), 6 deletions(-) create mode 100644 src/main/java/net/fabricmc/loom/task/prod/AbstractProductionRunTask.java create mode 100644 src/main/java/net/fabricmc/loom/task/prod/ClientProductionRunTask.java create mode 100644 src/main/java/net/fabricmc/loom/task/prod/ServerProductionRunTask.java diff --git a/gradle/runtime.libs.versions.toml b/gradle/runtime.libs.versions.toml index e35da821b..0e3f7cf19 100644 --- a/gradle/runtime.libs.versions.toml +++ b/gradle/runtime.libs.versions.toml @@ -10,6 +10,7 @@ dev-launch-injector = "0.2.1+build.8" terminal-console-appender = "1.3.0" jetbrains-annotations = "25.0.0" native-support = "1.0.1" +fabric-installer = "1.0.1" [libraries] # Decompilers @@ -22,4 +23,5 @@ mixin-compile-extensions = { module = "net.fabricmc:fabric-mixin-compile-extensi dev-launch-injector = { module = "net.fabricmc:dev-launch-injector", version.ref = "dev-launch-injector" } terminal-console-appender = { module = "net.minecrell:terminalconsoleappender", version.ref = "terminal-console-appender" } jetbrains-annotations = { module = "org.jetbrains:annotations", version.ref = "jetbrains-annotations" } -native-support = { module = "net.fabricmc:fabric-loom-native-support", version.ref = "native-support" } \ No newline at end of file +native-support = { module = "net.fabricmc:fabric-loom-native-support", version.ref = "native-support" } +fabric-installer = { module = "net.fabricmc:fabric-installer", version.ref = "fabric-installer" } \ No newline at end of file diff --git a/gradle/test.libs.versions.toml b/gradle/test.libs.versions.toml index f098d0109..357c1c285 100644 --- a/gradle/test.libs.versions.toml +++ b/gradle/test.libs.versions.toml @@ -8,7 +8,6 @@ mixin = "0.15.3+mixin.0.8.7" gradle-nightly = "8.13-20241222002427+0000" fabric-loader = "0.16.9" -fabric-installer = "1.0.1" [libraries] spock = { module = "org.spockframework:spock-core", version.ref = "spock" } @@ -19,5 +18,4 @@ mockito = { module = "org.mockito:mockito-core", version.ref = "mockito" } java-debug = { module = "com.microsoft.java:com.microsoft.java.debug.core", version.ref = "java-debug" } mixin = { module = "net.fabricmc:sponge-mixin", version.ref = "mixin" } gradle-nightly = { module = "org.gradle:dummy", version.ref = "gradle-nightly" } -fabric-loader = { module = "net.fabricmc:fabric-loader", version.ref = "fabric-loader" } -fabric-installer = { module = "net.fabricmc:fabric-installer", version.ref = "fabric-installer" } \ No newline at end of file +fabric-loader = { module = "net.fabricmc:fabric-loader", version.ref = "fabric-loader" } \ No newline at end of file diff --git a/src/main/java/net/fabricmc/loom/configuration/LoomConfigurations.java b/src/main/java/net/fabricmc/loom/configuration/LoomConfigurations.java index 9d2654431..8922f3afa 100644 --- a/src/main/java/net/fabricmc/loom/configuration/LoomConfigurations.java +++ b/src/main/java/net/fabricmc/loom/configuration/LoomConfigurations.java @@ -153,6 +153,11 @@ public void run() { getDependencies().add(Constants.Configurations.LOOM_DEVELOPMENT_DEPENDENCIES, LoomVersions.TERMINAL_CONSOLE_APPENDER.mavenNotation()); getDependencies().add(JavaPlugin.COMPILE_ONLY_CONFIGURATION_NAME, LoomVersions.JETBRAINS_ANNOTATIONS.mavenNotation()); getDependencies().add(JavaPlugin.TEST_COMPILE_ONLY_CONFIGURATION_NAME, LoomVersions.JETBRAINS_ANNOTATIONS.mavenNotation()); + + register(Constants.Configurations.MINECRAFT_TEST_CLIENT_RUNTIME_LIBRARIES, Role.RESOLVABLE); + extendsFrom(Constants.Configurations.MINECRAFT_TEST_CLIENT_RUNTIME_LIBRARIES, Constants.Configurations.MINECRAFT_NATIVES); + extendsFrom(Constants.Configurations.MINECRAFT_TEST_CLIENT_RUNTIME_LIBRARIES, Constants.Configurations.MINECRAFT_CLIENT_RUNTIME_LIBRARIES); + extendsFrom(Constants.Configurations.MINECRAFT_TEST_CLIENT_RUNTIME_LIBRARIES, Constants.Configurations.LOADER_DEPENDENCIES); } private NamedDomainObjectProvider register(String name, Role role) { diff --git a/src/main/java/net/fabricmc/loom/task/prod/AbstractProductionRunTask.java b/src/main/java/net/fabricmc/loom/task/prod/AbstractProductionRunTask.java new file mode 100644 index 000000000..15c454391 --- /dev/null +++ b/src/main/java/net/fabricmc/loom/task/prod/AbstractProductionRunTask.java @@ -0,0 +1,203 @@ +/* + * This file is part of fabric-loom, licensed under the MIT License (MIT). + * + * Copyright (c) 2025 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.prod; + +import java.io.File; +import java.io.IOException; +import java.nio.file.Files; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +import javax.inject.Inject; + +import org.gradle.api.artifacts.Configuration; +import org.gradle.api.artifacts.Dependency; +import org.gradle.api.file.ConfigurableFileCollection; +import org.gradle.api.file.DirectoryProperty; +import org.gradle.api.plugins.JavaPluginExtension; +import org.gradle.api.provider.ListProperty; +import org.gradle.api.provider.Property; +import org.gradle.api.provider.Provider; +import org.gradle.api.tasks.Classpath; +import org.gradle.api.tasks.Input; +import org.gradle.api.tasks.Internal; +import org.gradle.api.tasks.Nested; +import org.gradle.api.tasks.OutputDirectory; +import org.gradle.api.tasks.TaskAction; +import org.gradle.jvm.toolchain.JavaLauncher; +import org.gradle.jvm.toolchain.JavaToolchainService; +import org.gradle.jvm.toolchain.JavaToolchainSpec; +import org.gradle.process.ExecOperations; +import org.gradle.process.ExecResult; +import org.gradle.process.ExecSpec; +import org.jetbrains.annotations.ApiStatus; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import net.fabricmc.loom.configuration.InstallerData; +import net.fabricmc.loom.task.AbstractLoomTask; +import net.fabricmc.loom.task.RemapTaskConfiguration; +import net.fabricmc.loom.util.Constants; +import net.fabricmc.loom.util.gradle.GradleUtils; + +/** + * This is the base task for running the game in a "production" like environment. Using intermediary names, and not enabling development only features. + * + *

Do not use this task directly, use {@link ClientProductionRunTask} or {@link ServerProductionRunTask} instead. + */ +@ApiStatus.Experimental +public abstract sealed class AbstractProductionRunTask extends AbstractLoomTask permits ClientProductionRunTask, ServerProductionRunTask { + private static final Logger LOGGER = LoggerFactory.getLogger(AbstractProductionRunTask.class); + + /** + * A collection of mods that will be used when running the game. The mods must be remapped to run with intermediary names. + * + *

By default this includes the remapped jar. + */ + @Classpath + public abstract ConfigurableFileCollection getMods(); + + /** + * A list of additional JVM arguments to pass to the game. + */ + @Input + public abstract ListProperty getJvmArgs(); + + /** + * A list of additional program arguments to pass to the game. + */ + @Input + public abstract ListProperty getProgramArgs(); + + /** + * The directory to run the game in. + */ + @OutputDirectory + public abstract DirectoryProperty getRunDir(); + + /** + * The {@link JavaLauncher} to use when running the game, this can be used to specify a specific Java version to use. + * + *

See: Java Toolchains + * @return + */ + @Nested + public abstract Property getJavaLauncher(); + + // Internal options + @ApiStatus.Internal + @Classpath + protected abstract ConfigurableFileCollection getClasspath(); + + @ApiStatus.Internal + @Input + protected abstract Property getMainClass(); + + @Inject + protected abstract ExecOperations getExecOperations(); + + @Inject + protected abstract JavaToolchainService getJavaToolchainService(); + + @Inject + public AbstractProductionRunTask() { + JavaToolchainSpec defaultToolchain = getProject().getExtensions().getByType(JavaPluginExtension.class).getToolchain(); + getJavaLauncher().convention(getJavaToolchainService().launcherFor(defaultToolchain)); + getRunDir().convention(getProject().getLayout().getProjectDirectory().dir("run")); + + if (!GradleUtils.getBooleanProperty(getProject(), Constants.Properties.DONT_REMAP)) { + getMods().from(getProject().getTasks().named(RemapTaskConfiguration.REMAP_JAR_TASK_NAME)); + } + } + + @TaskAction + public void run() throws IOException { + Files.createDirectories(getRunDir().get().getAsFile().toPath()); + + ExecResult result = getExecOperations().exec(exec -> { + configureCommand(exec); + configureJvmArgs(exec); + configureClasspath(exec); + configureMainClass(exec); + configureProgramArgs(exec); + + exec.setWorkingDir(getRunDir()); + + LOGGER.debug("Running command: {}", exec.getCommandLine()); + }); + result.assertNormalExitValue(); + } + + protected void configureCommand(ExecSpec exec) { + exec.commandLine(getJavaLauncher().get().getExecutablePath()); + } + + protected void configureJvmArgs(ExecSpec exec) { + exec.args(getJvmArgs().get()); + exec.args("-Dfabric.addMods=" + joinFiles(getMods().getFiles().stream())); + } + + protected Stream streamClasspath() { + return getClasspath().getFiles().stream(); + } + + protected void configureClasspath(ExecSpec exec) { + exec.args("-cp"); + exec.args(joinFiles(streamClasspath())); + } + + protected void configureMainClass(ExecSpec exec) { + exec.args(getMainClass().get()); + } + + protected void configureProgramArgs(ExecSpec exec) { + exec.args(getProgramArgs().get()); + } + + @Internal + protected Provider getProjectLoaderVersion() { + return getProject().provider(() -> { + InstallerData installerData = getExtension().getInstallerData(); + + if (installerData == null) { + return null; + } + + return installerData.version(); + }); + } + + protected Provider detachedConfigurationProvider(String mavenNotation, Provider versionProvider) { + return versionProvider.map(version -> { + Dependency serverLauncher = getProject().getDependencies().create(mavenNotation.formatted(version)); + return getProject().getConfigurations().detachedConfiguration(serverLauncher); + }); + } + + private static String joinFiles(Stream stream) { + return stream.map(File::getAbsolutePath) + .collect(Collectors.joining(File.pathSeparator)); + } +} diff --git a/src/main/java/net/fabricmc/loom/task/prod/ClientProductionRunTask.java b/src/main/java/net/fabricmc/loom/task/prod/ClientProductionRunTask.java new file mode 100644 index 000000000..f608fea0c --- /dev/null +++ b/src/main/java/net/fabricmc/loom/task/prod/ClientProductionRunTask.java @@ -0,0 +1,93 @@ +/* + * This file is part of fabric-loom, licensed under the MIT License (MIT). + * + * Copyright (c) 2025 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.prod; + +import java.io.File; + +import javax.inject.Inject; + +import org.gradle.api.file.DirectoryProperty; +import org.gradle.api.provider.Property; +import org.gradle.api.tasks.Input; +import org.gradle.api.tasks.InputFiles; +import org.gradle.process.ExecSpec; +import org.jetbrains.annotations.ApiStatus; + +import net.fabricmc.loom.util.Constants; +import net.fabricmc.loom.util.Platform; + +/** + * A task that runs the Minecraft client in a similar way to a production launcher. You must manually register a task of this type to use it. + */ +@ApiStatus.Experimental +public abstract non-sealed class ClientProductionRunTask extends AbstractProductionRunTask { + // Internal options + @Input + protected abstract Property getAssetsIndex(); + + @InputFiles + protected abstract DirectoryProperty getAssetsDir(); + + @Inject + public ClientProductionRunTask() { + getAssetsIndex().set(getExtension().getMinecraftVersion() + .map(minecraftVersion -> getExtension() + .getMinecraftProvider() + .getVersionInfo() + .assetIndex() + .fabricId(minecraftVersion) + ) + ); + getAssetsDir().set(new File(getExtension().getFiles().getUserCache(), "assets")); + getMainClass().convention("net.fabricmc.loader.impl.launch.knot.KnotClient"); + + getClasspath().from(getExtension().getMinecraftProvider().getMinecraftClientJar()); + getClasspath().from(detachedConfigurationProvider("net.fabricmc:fabric-loader:%s", getProjectLoaderVersion())); + getClasspath().from(detachedConfigurationProvider("net.fabricmc:intermediary:%s", getExtension().getMinecraftVersion())); + getClasspath().from(getProject().getConfigurations().named(Constants.Configurations.MINECRAFT_TEST_CLIENT_RUNTIME_LIBRARIES)); + + dependsOn("downloadAssets"); + } + + @Override + protected void configureJvmArgs(ExecSpec exec) { + super.configureJvmArgs(exec); + + if (Platform.CURRENT.getOperatingSystem().isMacOS()) { + exec.args("-XstartOnFirstThread"); + } + } + + @Override + protected void configureProgramArgs(ExecSpec exec) { + super.configureProgramArgs(exec); + + exec.args( + "--assetIndex", getAssetsIndex().get(), + "--assetsDir", getAssetsDir().get().getAsFile().getAbsolutePath(), + "--gameDir", getRunDir().get().getAsFile().getAbsolutePath() + ); + } +} diff --git a/src/main/java/net/fabricmc/loom/task/prod/ServerProductionRunTask.java b/src/main/java/net/fabricmc/loom/task/prod/ServerProductionRunTask.java new file mode 100644 index 000000000..36a925ca5 --- /dev/null +++ b/src/main/java/net/fabricmc/loom/task/prod/ServerProductionRunTask.java @@ -0,0 +1,108 @@ +/* + * This file is part of fabric-loom, licensed under the MIT License (MIT). + * + * Copyright (c) 2025 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.prod; + +import java.io.File; +import java.io.IOException; +import java.util.stream.Stream; + +import javax.inject.Inject; + +import org.gradle.api.file.RegularFileProperty; +import org.gradle.api.provider.Property; +import org.gradle.api.tasks.Input; +import org.gradle.api.tasks.OutputFile; +import org.jetbrains.annotations.ApiStatus; + +import net.fabricmc.loom.util.LoomVersions; +import net.fabricmc.loom.util.ZipUtils; + +/** + * A task that runs the server using the production server launcher. You must manually register a task of this type to use it. + */ +@ApiStatus.Experimental +public abstract non-sealed class ServerProductionRunTask extends AbstractProductionRunTask { + /** + * The version of Fabric Loader to use. + * + *

Defaults to the version of Fabric Loader that the project is using. + */ + @Input + public abstract Property getLoaderVersion(); + + /** + * The version of Minecraft to use. + * + *

Defaults to the version of Minecraft that the project is using. + */ + @Input + public abstract Property getMinecraftVersion(); + + /** + * The version of the Fabric Installer to use. + * + *

Defaults to a version provided by Loom. + */ + @Input + public abstract Property getInstallerVersion(); + + // Internal options + + @ApiStatus.Internal + @OutputFile + public abstract RegularFileProperty getInstallPropertiesJar(); + + @Inject + public ServerProductionRunTask() { + getLoaderVersion().convention(getProjectLoaderVersion()); + getMinecraftVersion().convention(getExtension().getMinecraftVersion()); + getInstallPropertiesJar().convention(getProject().getLayout().getBuildDirectory().file("server_properties.jar")); + getInstallerVersion().convention(LoomVersions.FABRIC_INSTALLER.version()); + + getMainClass().convention("net.fabricmc.installer.ServerLauncher"); + getClasspath().from(detachedConfigurationProvider("net.fabricmc:fabric-installer:%s:server", getInstallerVersion())); + + getProgramArgs().add("nogui"); + } + + @Override + public void run() throws IOException { + ZipUtils.add( + getInstallPropertiesJar().get().getAsFile().toPath(), + "install.properties", + "fabric-loader-version=%s\ngame-version=%s".formatted(getLoaderVersion().get(), getMinecraftVersion().get()) + ); + + super.run(); + } + + @Override + protected Stream streamClasspath() { + return Stream.concat( + super.streamClasspath(), + Stream.of(getInstallPropertiesJar().get().getAsFile()) + ); + } +} diff --git a/src/main/java/net/fabricmc/loom/util/Constants.java b/src/main/java/net/fabricmc/loom/util/Constants.java index b197ee992..deceb58d3 100644 --- a/src/main/java/net/fabricmc/loom/util/Constants.java +++ b/src/main/java/net/fabricmc/loom/util/Constants.java @@ -82,6 +82,10 @@ public static final class Configurations { */ public static final String LOCAL_RUNTIME = "localRuntime"; public static final String NAMED_ELEMENTS = "namedElements"; + /** + * The configuration that contains the Minecraft client and loader runtime libraries, as used by the production run tasks. + */ + public static final String MINECRAFT_TEST_CLIENT_RUNTIME_LIBRARIES = "minecraftTestClientRuntimeLibraries"; private Configurations() { } diff --git a/src/test/groovy/net/fabricmc/loom/test/integration/RunConfigTest.groovy b/src/test/groovy/net/fabricmc/loom/test/integration/RunConfigTest.groovy index 0beb66d02..77ee9e503 100644 --- a/src/test/groovy/net/fabricmc/loom/test/integration/RunConfigTest.groovy +++ b/src/test/groovy/net/fabricmc/loom/test/integration/RunConfigTest.groovy @@ -1,7 +1,7 @@ /* * This file is part of fabric-loom, licensed under the MIT License (MIT). * - * Copyright (c) 2018-2023 FabricMC + * Copyright (c) 2018-2025 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 @@ -24,6 +24,7 @@ package net.fabricmc.loom.test.integration +import spock.lang.IgnoreIf import spock.lang.Specification import spock.lang.Unroll import spock.util.environment.RestoreSystemProperties @@ -130,4 +131,53 @@ class RunConfigTest extends Specification implements GradleProjectTestTrait { where: version << STANDARD_TEST_VERSIONS } + + @Unroll + def "prod server (gradle #version)"() { + setup: + def gradle = gradleProject(project: "minimalBase", version: version) + gradle.buildGradle << ''' + dependencies { + minecraft "com.mojang:minecraft:1.21.4" + mappings "net.fabricmc:yarn:1.21.4+build.4:v2" + modImplementation "net.fabricmc:fabric-loader:0.16.9" + } + + tasks.register("prodServer", net.fabricmc.loom.task.prod.ServerProductionRunTask) { + installerVersion = "1.0.1" + } + ''' + when: + def result = gradle.run(task: "prodServer") + + then: + result.task(":prodServer").outcome == SUCCESS + + where: + version << STANDARD_TEST_VERSIONS + } + + @Unroll + @IgnoreIf({ System.getenv("CI") != null }) // This test is disabled on CI because it launches a real client and cannot run headless. + def "prod client (gradle #version)"() { + setup: + def gradle = gradleProject(project: "minimalBase", version: version) + gradle.buildGradle << ''' + dependencies { + minecraft "com.mojang:minecraft:1.21.4" + mappings "net.fabricmc:yarn:1.21.4+build.4:v2" + modImplementation "net.fabricmc:fabric-loader:0.16.9" + } + + tasks.register("prodClient", net.fabricmc.loom.task.prod.ClientProductionRunTask) + ''' + when: + def result = gradle.run(task: "prodClient") + + then: + result.task(":prodClient").outcome == SUCCESS + + where: + version << STANDARD_TEST_VERSIONS + } } diff --git a/src/test/groovy/net/fabricmc/loom/test/util/ServerRunner.groovy b/src/test/groovy/net/fabricmc/loom/test/util/ServerRunner.groovy index 805c2094d..38d13525e 100644 --- a/src/test/groovy/net/fabricmc/loom/test/util/ServerRunner.groovy +++ b/src/test/groovy/net/fabricmc/loom/test/util/ServerRunner.groovy @@ -29,11 +29,12 @@ import java.util.concurrent.TimeUnit import groovy.transform.Immutable import net.fabricmc.loom.test.LoomTestVersions +import net.fabricmc.loom.util.LoomVersions import net.fabricmc.loom.util.download.Download class ServerRunner { static final String LOADER_VERSION = LoomTestVersions.FABRIC_LOADER.version() - static final String INSTALLER_VERSION = LoomTestVersions.FABRIC_INSTALLER.version() + static final String INSTALLER_VERSION = LoomVersions.FABRIC_INSTALLER.version() static final Map FABRIC_API_URLS = [ "1.16.5": "https://github.com/FabricMC/fabric/releases/download/0.37.1%2B1.16/fabric-api-0.37.1+1.16.jar", "1.17.1": "https://github.com/FabricMC/fabric/releases/download/0.37.1%2B1.17/fabric-api-0.37.1+1.17.jar"