From b5c9fb1c04cc59c30406a43c017ce03edb590f1e Mon Sep 17 00:00:00 2001 From: Sebastian Hartte Date: Mon, 25 Nov 2024 00:28:53 +0100 Subject: [PATCH] Getting the production client to launch --- .github/workflows/build-prs.yml | 3 + .../net/neoforged/neodev/NeoDevPlugin.java | 16 +++- .../neodev/e2e/RunProductionClient.java | 10 ++- .../neodev/e2e/TestProductionClient.java | 31 ++++++++ .../neoforge/client/ClientNeoForgeMod.java | 3 + .../neoforge/common/NeoForgeMod.java | 39 +-------- .../neoforge/common/util/SelfTest.java | 79 +++++++++++++++++++ 7 files changed, 140 insertions(+), 41 deletions(-) create mode 100644 buildSrc/src/main/java/net/neoforged/neodev/e2e/TestProductionClient.java create mode 100644 src/main/java/net/neoforged/neoforge/common/util/SelfTest.java diff --git a/.github/workflows/build-prs.yml b/.github/workflows/build-prs.yml index 1f945197d9..d0b6dcefde 100644 --- a/.github/workflows/build-prs.yml +++ b/.github/workflows/build-prs.yml @@ -53,6 +53,9 @@ jobs: - name: Build with Gradle run: ./gradlew assemble checkFormatting + - name: Test Production Client + run: ./gradlew testProductionClient + - name: Run JCC if: ${{ ! startsWith(github.event.pull_request.head.ref, 'port/') && ! startsWith(github.ref_name, 'port/') && ! startsWith(github.event.pull_request.base.ref, 'port/') }} run: ./gradlew checkJarCompatibility diff --git a/buildSrc/src/main/java/net/neoforged/neodev/NeoDevPlugin.java b/buildSrc/src/main/java/net/neoforged/neodev/NeoDevPlugin.java index f21963de53..6f060f0f53 100644 --- a/buildSrc/src/main/java/net/neoforged/neodev/NeoDevPlugin.java +++ b/buildSrc/src/main/java/net/neoforged/neodev/NeoDevPlugin.java @@ -5,6 +5,7 @@ import net.neoforged.moddevgradle.tasks.JarJar; import net.neoforged.neodev.e2e.InstallProductionClient; import net.neoforged.neodev.e2e.RunProductionClient; +import net.neoforged.neodev.e2e.TestProductionClient; import net.neoforged.neodev.installer.CreateArgsFile; import net.neoforged.neodev.installer.CreateInstallerProfile; import net.neoforged.neodev.installer.CreateLauncherProfile; @@ -35,6 +36,7 @@ import java.util.ArrayList; import java.util.List; import java.util.Map; +import java.util.function.Consumer; public class NeoDevPlugin implements Plugin { static final String GROUP = "neoforge development"; @@ -567,15 +569,23 @@ private void setupEndToEndTesting(Project project, task.getInstallationDir().set(destinationDir); }); - var runClient = project.getTasks().register("runProductionClient", RunProductionClient.class, task -> { - task.setGroup(INTERNAL_GROUP); - task.setDescription("Runs the production client installed by installProductionClient."); + Consumer configureRunProductionClient = task -> { task.getLibraryFiles().addAll(IdentifiedFile.listFromConfiguration(project, configurations.neoFormClasspath)); task.getLibraryFiles().addAll(IdentifiedFile.listFromConfiguration(project, configurations.launcherProfileClasspath)); task.getAssetPropertiesFile().set(downloadAssets.flatMap(DownloadAssets::getAssetPropertiesFile)); task.getMinecraftVersion().set(minecraftVersion); task.getNeoForgeVersion().set(neoForgeVersion); task.getInstallationDir().set(installClient.flatMap(InstallProductionClient::getInstallationDir)); + }; + project.getTasks().register("runProductionClient", RunProductionClient.class, task -> { + task.setGroup(INTERNAL_GROUP); + task.setDescription("Runs the production client installed by installProductionClient."); + configureRunProductionClient.accept(task); + }); + project.getTasks().register("testProductionClient", TestProductionClient.class, task -> { + task.setGroup(INTERNAL_GROUP); + task.setDescription("Tests the production client installed by installProductionClient."); + configureRunProductionClient.accept(task); }); } diff --git a/buildSrc/src/main/java/net/neoforged/neodev/e2e/RunProductionClient.java b/buildSrc/src/main/java/net/neoforged/neodev/e2e/RunProductionClient.java index 2c59b65de3..93bc0520fa 100644 --- a/buildSrc/src/main/java/net/neoforged/neodev/e2e/RunProductionClient.java +++ b/buildSrc/src/main/java/net/neoforged/neodev/e2e/RunProductionClient.java @@ -129,7 +129,15 @@ public void exec() { execOperations.javaexec(spec -> { // The JVM args at this point may include debugging options when started through IntelliJ - spec.jvmArgs(getAllJvmArgs()); + spec.jvmArgs(getJvmArguments().get()); + spec.environment(getEnvironment()); + spec.debugOptions(debug -> { + debug.getEnabled().set(getDebugOptions().getEnabled()); + debug.getPort().set(getDebugOptions().getPort()); + debug.getSuspend().set(getDebugOptions().getSuspend()); + debug.getServer().set(getDebugOptions().getServer()); + debug.getHost().set(getDebugOptions().getHost()); + }); applyVersionManifest(installDir, "neoforge-" + neoForgeVersion, placeholders, librariesDir, spec); }); } diff --git a/buildSrc/src/main/java/net/neoforged/neodev/e2e/TestProductionClient.java b/buildSrc/src/main/java/net/neoforged/neodev/e2e/TestProductionClient.java new file mode 100644 index 0000000000..95313f3d1f --- /dev/null +++ b/buildSrc/src/main/java/net/neoforged/neodev/e2e/TestProductionClient.java @@ -0,0 +1,31 @@ +package net.neoforged.neodev.e2e; + +import org.gradle.api.GradleException; +import org.gradle.process.ExecOperations; + +import javax.inject.Inject; +import java.io.File; +import java.time.Duration; +import java.time.temporal.ChronoUnit; + +public abstract class TestProductionClient extends RunProductionClient { + @Inject + public TestProductionClient(ExecOperations execOperations) { + super(execOperations); + + getTimeout().set(Duration.of(5, ChronoUnit.MINUTES)); + } + + @Override + public void exec() { + var selfTestReport = new File(getTemporaryDir(), "client_self_test.txt"); + + environment("NEOFORGE_CLIENT_SELFTEST", selfTestReport.getAbsolutePath()); + + super.exec(); + + if (!selfTestReport.exists()) { + throw new GradleException("Missing self test report file after running client: " + selfTestReport); + } + } +} diff --git a/src/main/java/net/neoforged/neoforge/client/ClientNeoForgeMod.java b/src/main/java/net/neoforged/neoforge/client/ClientNeoForgeMod.java index d55c15caec..f8526f30d3 100644 --- a/src/main/java/net/neoforged/neoforge/client/ClientNeoForgeMod.java +++ b/src/main/java/net/neoforged/neoforge/client/ClientNeoForgeMod.java @@ -42,6 +42,7 @@ import net.neoforged.neoforge.common.ModConfigSpec; import net.neoforged.neoforge.common.NeoForge; import net.neoforged.neoforge.common.NeoForgeMod; +import net.neoforged.neoforge.common.util.SelfTest; import net.neoforged.neoforge.internal.versions.neoforge.NeoForgeVersion; import org.jetbrains.annotations.ApiStatus; @@ -49,6 +50,8 @@ @Mod(value = "neoforge", dist = Dist.CLIENT) public class ClientNeoForgeMod { public ClientNeoForgeMod(IEventBus modEventBus, ModContainer container) { + SelfTest.initClient(); + ClientCommandHandler.init(); TagConventionLogWarningClient.init(); diff --git a/src/main/java/net/neoforged/neoforge/common/NeoForgeMod.java b/src/main/java/net/neoforged/neoforge/common/NeoForgeMod.java index 4e4230f73b..67b0c3318a 100644 --- a/src/main/java/net/neoforged/neoforge/common/NeoForgeMod.java +++ b/src/main/java/net/neoforged/neoforge/common/NeoForgeMod.java @@ -9,13 +9,9 @@ import com.mojang.serialization.Codec; import com.mojang.serialization.MapCodec; import com.mojang.serialization.codecs.RecordCodecBuilder; - -import java.io.FileOutputStream; -import java.io.IOException; import java.util.EnumSet; import java.util.List; import java.util.Optional; -import java.util.Properties; import java.util.Set; import java.util.UUID; import java.util.function.Function; @@ -83,7 +79,6 @@ import net.neoforged.fml.config.ModConfigs; import net.neoforged.fml.event.lifecycle.FMLCommonSetupEvent; import net.neoforged.fml.event.lifecycle.FMLLoadCompleteEvent; -import net.neoforged.fml.loading.FMLLoader; import net.neoforged.fml.loading.progress.StartupNotificationManager; import net.neoforged.neoforge.capabilities.CapabilityHooks; import net.neoforged.neoforge.common.advancements.critereon.ItemAbilityPredicate; @@ -127,6 +122,7 @@ import net.neoforged.neoforge.common.loot.CanItemPerformAbility; import net.neoforged.neoforge.common.loot.IGlobalLootModifier; import net.neoforged.neoforge.common.loot.LootTableIdCondition; +import net.neoforged.neoforge.common.util.SelfTest; import net.neoforged.neoforge.common.world.BiomeModifier; import net.neoforged.neoforge.common.world.BiomeModifiers; import net.neoforged.neoforge.common.world.BiomeModifiers.AddFeaturesBiomeModifier; @@ -139,7 +135,6 @@ import net.neoforged.neoforge.common.world.StructureModifiers; import net.neoforged.neoforge.data.event.GatherDataEvent; import net.neoforged.neoforge.event.server.ServerStoppingEvent; -import net.neoforged.neoforge.event.tick.ServerTickEvent; import net.neoforged.neoforge.fluids.BaseFlowingFluid; import net.neoforged.neoforge.fluids.CauldronFluidContent; import net.neoforged.neoforge.fluids.FluidType; @@ -531,7 +526,7 @@ public NeoForgeMod(IEventBus modEventBus, Dist dist, ModContainer container) { LOGGER.info(NEOFORGEMOD, "NeoForge mod loading, version {}, for MC {}", NeoForgeVersion.getVersion(), DetectedVersion.BUILT_IN.getName()); ForgeSnapshotsMod.logStartupWarning(); - handleSelfTest(); + SelfTest.initCommon(); CrashReportCallables.registerCrashCallable("Crash Report UUID", () -> { final UUID uuid = UUID.randomUUID(); @@ -694,36 +689,6 @@ private static boolean isPRBuild(String neoVersion) { return neoVersion.matches("\\d+\\.\\d+\\.\\d+(-beta)?-pr-\\d+-[\\w-]+"); } - private static void handleSelfTest() { - String selfTestDestination = System.getenv("NEOFORGE_DEDICATED_SERVER_SELFTEST"); - if (selfTestDestination != null) { - if (FMLLoader.getDist() != Dist.DEDICATED_SERVER) { - LOGGER.error("The server self-test ran with a dist of {} instead of dedicated server!", FMLLoader.getDist()); - System.exit(1); - } - NeoForge.EVENT_BUS.addListener((ServerTickEvent.Pre e) -> writeSelfTestReport(selfTestDestination)); - } - } - - /** - * This is used by our GitHub Actions pipeline to run an E2E test for PRs. - * It writes a small self-test report to the file indicated by the system property and exits. - */ - private static void writeSelfTestReport(String path) { - try (var out = new FileOutputStream(path)) { - Properties p = new Properties(); - p.setProperty("neoforge_version", NeoForgeVersion.getVersion()); - p.setProperty("minecraft_version", DetectedVersion.BUILT_IN.getName()); - p.store(out, ""); - } catch (IOException e) { - LOGGER.error("Failed to write self-test to '{}'", path, e); - System.exit(1); - } - - LOGGER.info("Exiting after writing self-test to '{}'", path); - System.exit(0); - } - public static boolean isPRBuild() { return isPRBuild; } diff --git a/src/main/java/net/neoforged/neoforge/common/util/SelfTest.java b/src/main/java/net/neoforged/neoforge/common/util/SelfTest.java new file mode 100644 index 0000000000..1ac689763a --- /dev/null +++ b/src/main/java/net/neoforged/neoforge/common/util/SelfTest.java @@ -0,0 +1,79 @@ +/* + * Copyright (c) NeoForged and contributors + * SPDX-License-Identifier: LGPL-2.1-only + */ + +package net.neoforged.neoforge.common.util; + +import net.minecraft.DetectedVersion; +import net.minecraft.client.Minecraft; +import net.minecraft.client.gui.screens.LoadingOverlay; +import net.minecraft.client.gui.screens.Overlay; +import net.minecraft.server.dedicated.DedicatedServer; +import net.neoforged.api.distmarker.Dist; +import net.neoforged.fml.loading.FMLLoader; +import net.neoforged.neoforge.client.event.ClientTickEvent; +import net.neoforged.neoforge.common.NeoForge; +import net.neoforged.neoforge.event.tick.ServerTickEvent; +import net.neoforged.neoforge.internal.versions.neoforge.NeoForgeVersion; +import org.jetbrains.annotations.ApiStatus; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.io.FileOutputStream; +import java.io.IOException; +import java.util.Properties; + +@ApiStatus.Internal +public final class SelfTest { + private static final Logger LOGGER = LoggerFactory.getLogger(SelfTest.class); + + private SelfTest() { + } + + public static void initClient() { + var clientSelfTestDestination = System.getenv("NEOFORGE_CLIENT_SELFTEST"); + if (clientSelfTestDestination != null) { + NeoForge.EVENT_BUS.addListener((ClientTickEvent.Pre e) -> { + if (Minecraft.getInstance().getOverlay() instanceof LoadingOverlay) { + return; + } + writeSelfTestReport(clientSelfTestDestination); + Minecraft.getInstance().getSoundManager().emergencyShutdown(); + Minecraft.getInstance().stop(); + }); + } + } + + public static void initCommon() { + var serverSelfTestDestination = System.getenv("NEOFORGE_DEDICATED_SERVER_SELFTEST"); + if (serverSelfTestDestination != null) { + if (FMLLoader.getDist() != Dist.DEDICATED_SERVER) { + LOGGER.error("The server self-test ran with a dist of {} instead of dedicated server!", FMLLoader.getDist()); + System.exit(1); + } + NeoForge.EVENT_BUS.addListener((ServerTickEvent.Pre e) -> { + writeSelfTestReport(serverSelfTestDestination); + }); + } + } + + /** + * This is used by our GitHub Actions pipeline to run an E2E test for PRs. + * It writes a small self-test report to the file indicated by the system property and exits. + */ + private static void writeSelfTestReport(String path) { + try (var out = new FileOutputStream(path)) { + Properties p = new Properties(); + p.setProperty("neoforge_version", NeoForgeVersion.getVersion()); + p.setProperty("minecraft_version", DetectedVersion.BUILT_IN.getName()); + p.store(out, ""); + } catch (IOException e) { + LOGGER.error("Failed to write self-test to '{}'", path, e); + System.exit(1); + } + + LOGGER.info("Exiting after writing self-test to '{}'", path); + System.exit(0); + } +}