diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 5b8eac92b..46c717cf3 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -123,14 +123,16 @@ jobs: - name: Run exact packaged integrated-world client smoke run: >- - timeout 15m xvfb-run -a ./gradlew packagedClientStartupSmoke + timeout --kill-after=30s 15m xvfb-run -a ./gradlew packagedClientStartupSmoke + -PvirtualDisplaySmoke=true -PpackagedForgeClientRuntime=${{ steps.forge-runtimes.outputs.client_runtime }} -PorespawnVerificationRepository=${{ steps.orespawn.outputs.repository }} --no-daemon --stacktrace - name: Run bounded development integrated-world client smoke run: >- - timeout 15m xvfb-run -a ./gradlew runClient + timeout --kill-after=30s 15m xvfb-run -a ./gradlew runClient + -PvirtualDisplaySmoke=true -PintegratedWorldSmoke=true -PintegratedWorldSmokeName=basemetals-ci-${{ github.run_id }} -PorespawnVerificationRepository=${{ steps.orespawn.outputs.repository }} diff --git a/CHANGELOG.txt b/CHANGELOG.txt index 1ca3b4d5a..cca6c2cd9 100644 --- a/CHANGELOG.txt +++ b/CHANGELOG.txt @@ -10,6 +10,7 @@ Base Metals 3.1.1.121011 - Update recipes, tags, loot, advancements and enchantments for Minecraft 1.21's data formats. - Keep arrows and bolts tied to their firing weapon so native enchantments apply after material damage. - Use the published OreSpawn 4.1.0.121011 release with the same eleven ore rules and copper defaults. +- Keep automated client checks from hanging at Forge's splash screen on virtual displays. Base Metals 3.1.0.120061 diff --git a/README.md b/README.md index d9cbe9857..1fe869cdd 100644 --- a/README.md +++ b/README.md @@ -194,6 +194,10 @@ identity checks, and a second clean build to prove byte-identical release artifacts. GameTests, smoke probes, migration fixtures, and reference sources are isolated from the published JAR. +Virtual-display client checks use `-PvirtualDisplaySmoke=true` to skip Forge's +early splash window. Minecraft still opens its game window and runs the full +world check. Ordinary client launches keep the normal splash screen. + Release artifacts use Maven coordinate `zone.moddev.mc.basemetals:BaseMetals:3.1.1.121011`. diff --git a/gradle/README.md b/gradle/README.md index 55a9ecc5a..83435058a 100644 --- a/gradle/README.md +++ b/gradle/README.md @@ -38,3 +38,9 @@ Run `prepareEclipse` after importing the project or refreshing Gradle. It generates the launches, copies processed resources and checks that ordinary runs cannot see tests or probes. Client launches explicitly select the client side of ForgeGradle's launcher so it resolves assets and native libraries. + +The two CI client smokes pass `-PvirtualDisplaySmoke=true`. This disables +Forge's early splash window only in their disposable run directories; the +game window and integrated-world assertions still run. Leave this option off +for ordinary launches. `verifyVirtualDisplaySmokeSetup` checks both paths as +part of `check`. diff --git a/gradle/verification/packaged-runtime.gradle b/gradle/verification/packaged-runtime.gradle index 1add56d10..3d0566966 100644 --- a/gradle/verification/packaged-runtime.gradle +++ b/gradle/verification/packaged-runtime.gradle @@ -17,6 +17,56 @@ def packagedClientJavaExecutable = providers.gradleProperty('packagedClientJavaE def packagedServerRunDirectory = layout.buildDirectory.dir('packaged-server-run') def packagedClientRunDirectory = layout.buildDirectory.dir('packaged-client-run') +def virtualDisplaySmoke = providers.gradleProperty('virtualDisplaySmoke') + .map { it.toBoolean() }.getOrElse(false) + +def configureVirtualDisplayWindow = { File runDirectory, boolean enabled -> + if (!enabled) { + return + } + + // Skip Forge's splash under Xvfb; Minecraft still opens its normal game window. + File config = new File(runDirectory, 'config/fml.toml') + config.parentFile.mkdirs() + config.setText('earlyWindowControl = false\n', 'UTF-8') +} + +def verifyVirtualDisplaySmokeSetup = tasks.register('verifyVirtualDisplaySmokeSetup') { + group = 'verification' + description = 'Checks that virtual-display settings stay confined to opted-in smoke runs.' + + doLast { + File root = layout.buildDirectory.dir('verification/virtual-display-config').get().asFile + File ordinaryRun = new File(root, 'ordinary') + File virtualRun = new File(root, 'virtual') + delete root + + configureVirtualDisplayWindow(ordinaryRun, false) + if (ordinaryRun.exists()) { + throw new GradleException('Ordinary launches must not receive the smoke-only Forge config') + } + + configureVirtualDisplayWindow(virtualRun, true) + File config = new File(virtualRun, 'config/fml.toml') + if (config.getText('UTF-8') != 'earlyWindowControl = false\n') { + throw new GradleException('Virtual-display smokes must disable only the early splash window') + } + } +} + +tasks.named('check') { + dependsOn verifyVirtualDisplaySmokeSetup +} + +tasks.matching { it.name == 'runClient' }.configureEach { + if (providers.gradleProperty('integratedWorldSmoke').isPresent()) { + doFirst { + configureVirtualDisplayWindow(layout.projectDirectory.dir('run-integrated-smoke').asFile, + virtualDisplaySmoke) + } + } +} + def requireRuntimeDirectory = { Provider configuredPath, String propertyName -> if (!configuredPath.isPresent()) { throw new GradleException("Pass -P${propertyName}=") @@ -163,6 +213,7 @@ def preparePackagedClientSmoke = tasks.register('preparePackagedClientSmoke') { } } new File(runDirectory, 'options.txt').setText('fullscreen:false\nlang:en_us\nonboardAccessibility:false\n', 'UTF-8') + configureVirtualDisplayWindow(runDirectory, virtualDisplaySmoke) if (providers.gradleProperty('packagedHostTextureProbe').isPresent()) { File runtime = requireRuntimeDirectory(packagedForgeClientRuntime, 'packagedForgeClientRuntime') File hostPack = new File(runDirectory, 'resourcepacks/basemetals-host-test')