Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,10 @@ internal fun AEvents.ArchieGameTestBuilder.archieTestGameTests()
{
client {
register<TestScreenGameTest>()
register<ComposeItemContainerMenuClientTests>()
}
server {
register<ComposeItemContainerMenuTests>()
}
server {
register<DataAttachmentTests>()
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
package net.kernelpanicsoft.archie.test.gametest

import dev.architectury.registry.menu.MenuRegistry
import net.kernelpanicsoft.archie.gametest.*
import net.kernelpanicsoft.archie.gui.item.PlayerInventoryItemAccess
import net.kernelpanicsoft.archie.test.TestItemContainerScreen
import net.kernelpanicsoft.archie.test.TestItemMenu
import net.minecraft.network.chat.Component
import net.minecraft.world.SimpleMenuProvider
import net.minecraft.world.item.ItemStack
import net.minecraft.world.item.Items

/**
* End-to-end coverage for [TestItemMenu]'s excluded-slot behavior, opened through the *real*
* server-to-client menu flow ([MenuRegistry.openExtendedMenu] + a real [TestItemContainerScreen]
* layout pass) - unlike [ComposeItemContainerMenuTests], which constructs [TestItemMenu] directly
* and so can't exercise anything depending on
* [net.kernelpanicsoft.archie.gui.ComposeContainerMenuBase.updateSlotData] actually having run
* (that method unconditionally sends an outbound `SlotData` packet, which throws from a
* server-only GameTest since packet registration itself is skipped there - see that class's KDoc).
* A client GameTest has both sides' networking registered for real, so this is where that gap is
* actually covered.
*/
@Suppress("unused")
class ComposeItemContainerMenuClientTests
{
@ClientGameTest
fun ClientGameTestContext.testExcludedSlotBehaviorViaRealMenuOpen()
{
withWorld {

Check failure on line 30 in Archie-Test/common/src/main/gametest/net/kernelpanicsoft/archie/test/gametest/ComposeItemContainerMenuClientTests.kt

View workflow job for this annotation

GitHub Actions / JUnit Test Report (neoforge)

ComposeItemContainerMenuClientTests.testExcludedSlotBehaviorViaRealMenuOpen [neoforge:client]

java.lang.AssertionError: GameTest failed: testExcludedSlotBehaviorViaRealMenuOpen [neoforge:client] Exit code: 1 Command: /home/runner/work/Archie/Archie/Archie-Test/gradlew :neoforge:runGametestClient --console=plain --no-daemon Log file: /home/runner/work/Archie/Archie/Archie-Test/build/tmp/junit-gametest-runner/neoforge-client.log --- Log tail --- at TRANSFORMER/minecraft@1.21.1/net.minecraft.client.Minecraft.runTick(Minecraft.java:1155) ~[neoforge-21.1.80-minecraft-merged-74f566db8d-1.21.1-loom.mappings.1_21_1.layered+hash.1146786786-v2-neoforge-21.1.80.jar%23179!/:?] {re:mixin,pl:unprotect:A,pl:runtimedistcleaner:A,re:classloading,pl:mixin:A,pl:unprotect:A,pl:runtimedistcleaner:A} at TRANSFORMER/minecraft@1.21.1/net.minecraft.client.Minecraft.run(Minecraft.java:807) ~[neoforge-21.1.80-minecraft-merged-74f566db8d-1.21.1-loom.mappings.1_21_1.layered+hash.1146786786-v2-neoforge-21.1.80.jar%23179!/:?] {re:mixin,pl:unprotect:A,pl:runtimedistcleaner:A,re:classloading,pl:mixin:A,pl:unprotect:A,pl:runtimedistcleaner:A} at TRANSFORMER/minecraft@1.21.1/net.minecraft.client.main.Main.main(Main.java:230) ~[neoforge-21.1.80-minecraft-merged-74f566db8d-1.21.1-loom.mappings.1_21_1.layered+hash.1146786786-v2-neoforge-21.1.80.jar%23179!/:?] {re:classloading,pl:unprotect:A,pl:runtimedistcleaner:A} at java.base/jdk.internal.reflect.DirectMethodHandleAccessor.invoke(DirectMethodHandleAccessor.java:103) ~[?:?] {} at java.base/java.lang.reflect.Method.invoke(Method.java:580) ~[?:?] {re:mixin} at MC-BOOTSTRAP/fml_loader@4.0.31/net.neoforged.fml.loading.targets.CommonLaunchHandler.runTarget(CommonLaunchHandler.java:136) ~[loader-4.0.31.jar%2390!/:4.0] {} at MC-BOOTSTRAP/fml_loader@4.0.31/net.neoforged.fml.loading.targets.CommonLaunchHandler.clientService(CommonLaunchHandler.java:124) ~[loader-4.0.31.jar%2390!/:4.0] {} at MC-BOOTSTRAP/fml_loader@4.0.31/net.neoforged.fml.loading.targets.NeoForgeClientUserdevLaunchHandler.runService(NeoForgeClientUserdevLaunchHandler.java:23) ~[loader-4.0.31.jar%2390!/:4.0] {} at MC-BOOTSTRAP/fml_loader@4.0.31/net.neoforged.fml.loading.targets.CommonLaunchHandler.lambda$launchService$4(CommonLaunchHandler.java:118) ~[loader-4.0.31.jar%2390!/:4.0] {} at MC-BOOTSTRAP/cpw.mods.modlauncher@11.0.4/cpw.mods.modlauncher.LaunchServiceHandlerDecorator.launch(LaunchServiceHandlerDecorator.java:30) [modlauncher-11.0.4.jar%2392!/:?] {} at MC-BOOTSTRAP/cpw.mods.modlauncher@11.0.4/cpw.mods.modlauncher.LaunchServiceHandler.launch(LaunchServiceHandler.java:53) [modlauncher-11.0.4.jar%2392!/:?] {} at MC-BOOTSTRAP/cpw.mods.modlauncher@11.0.4/cpw.mods.modlauncher.LaunchServiceHandler.launch(LaunchServiceHandler.java:71) [modlauncher-11.0.4.jar%2392!/:?] {} at MC-BOOTSTRAP/cpw.mods.modlauncher@11.0.4/cpw.mods.modlauncher.Launcher.run(Launcher.java:103) [modlauncher-11.0.4.jar%2392!/:?] {} at MC-BOOTSTRAP/cpw.mods.modlauncher@11.0.4/cpw.mods.modlauncher.Launcher.main(Launcher.java:74) [modlauncher-11.0.4.jar%2392!/:?] {} at MC-BOOTSTRAP/cpw.mods.modlauncher@11.0.4/cpw.mods.modlauncher.BootstrapLaunchConsumer.accept(BootstrapLaunchConsumer.java:26) [modlauncher-11.0.4.jar%2392!/:?] {} at MC-BOOTSTRAP/cpw.mods.modlauncher@11.0.4/cpw.mods.modlauncher.BootstrapLaunchConsumer.accept(BootstrapLaunchConsumer.java:23) [modlauncher-11.0.4.jar%2392!/:?] {} at cpw.mods.bootstraplauncher.BootstrapLauncher.run(BootstrapLauncher.java:210) [bootstraplauncher-2.0.2.jar:?] {} at cpw.mods.bootstraplauncher.BootstrapLauncher.main(BootstrapLauncher.java:69) [bootstraplauncher-2.0.2.jar:?] {} at net.fabricmc.devlaunchinjector.Main.main(Main.java:86) [dev-launch-injector-0.2.1+build.8.jar:?] {} [15:43:14] [Render thread/INFO] [minecraft/RecipeManager]: Loaded 1277 recipes [15:43:15] [Render thread/INFO] [minecraft/AdvancementTree]: Loaded 1399 advancements [15:43:15] [Render thread/ERROR] [ne.ne.fm.ModLoader/]: Cowardly refusing to send event net.neoforged.neoforge.event.AddPackFindersEvent to a broken mod state [15:43:15] [Server thread/INFO] [minecraft/IntegratedServer]: Starting integrated minecraft server version 1.21.1 [15:43:15] [Server thread/INFO] [minecraft/MinecraftServer]: Generating keypair [15:43:15] [Server thread/ERROR] [minecraft/MinecraftServer]: Encountered an unexpected exception java.lang.IllegalStateException: Missing registry: ResourceKey[minecraft:root / neoforge:biome_modifier] at TRANSFORMER/minecraft@1.21.1/net.minecraft.core.RegistryAccess.method_30534(RegistryAccess.java:26) ~[neoforge-21.1.80-minecraft-merged-74f566db8d-1.21.1-loom.mappings.1_21_1.layered+hash.1146786786-v2-neoforge-21.1.80.jar%23179!/:?] {re:mixin,pl:unprotect:A,re:classloading,pl:unprotect:A} at java.base/java.util.Optional.orElseThrow(Optional.java:403) ~[?:?] {re:computing_frames,re:mixin} at TRANSFORMER/minecraft@1.21.1/net.minecraft.core.RegistryAccess.registryOrThrow(RegistryAccess.java:26) ~[neoforge-21.1.80-minecraft-merged-74f566db8d-1.21.1-loom.mappings.1_21_1.layered+hash.1146786786-v2-neoforge-21.1.80.jar%23179!/:?] {re:mixin,pl:unprotect:A,re:classloading,pl:unprotect:A} at TRANSFORMER/neoforge@21.1.80/net.neoforged.neoforge.server.ServerLifecycleHooks.runModifiers(ServerLifecycleHooks.java:164) ~[neoforge-21.1.80-minecraft-merged-74f566db8d-1.21.1-loom.mappings.1_21_1.layered+hash.1146786786-v2-neoforge-21.1.80.jar%23180!/:?] {re:classloading} at TRANSFORMER/neoforge@21.1.80/net.neoforged.neoforge.server.ServerLifecycleHooks.handleServerAboutToStart(ServerLifecycleHooks.java:98) ~[neoforge-21.1.80-minecraft-merged-74f566db8d-1.21.1-loom.mappings.1_21_1.layered+hash.1146786786-v2-neoforge-21.1.80.jar%23180!/:?] {re:classloading} at TRANSFORMER/minecraft@1.21.1/net.minecraft.client.server.IntegratedServer.initServer(IntegratedServer.java:76) ~[neoforge-21.1.80-minecraft-merged-74f566db8d-1.21.1-loom.mappings.1_21_1.layered+hash.1146786786-v2-neoforge-21.1.80.jar%23179!/:?] {re:classloading,pl:unprotect:A,pl:runtimedistcleaner:A} at TRANSFORMER/minecraft@1.21.1/net.minecraft.server.MinecraftServer.runServer(MinecraftServer.java:670) ~[neoforge-21.1.80-minecraft-merged-74f566db8d-1.21.1-loom.mappings.1_21_1.layered+hash.1146786786-v2-neoforge-21.1.80.jar%23179!/:?] {re:mixin,pl:accesstransformer:B,pl:unprotect:A,re:classloading,pl:accesstransformer:B,pl:mixin:APP:archie.mixins.json:threading.ServerMixin from mod archie,pl:mixin:A,pl:unprotect:A} at TRANSFORMER/minecraft@1.21.1/net.minecraft.server.MinecraftServer.method_29739(MinecraftServer.java:267) ~[neoforge-21.1.80-minecraft-merged-74f566db8d-1.21.1-loom.mappings.1_21_1.layered+hash.1146786786-v2-neoforge-21.1.80.jar%23179!/:?] {re:mixin,pl:accesstransformer:B,pl:unprotect:A,re:classloading,pl:accesstransformer:B,pl:mixin:APP:archie.mixins.json:threading.ServerMixin from mod archie,pl:mixin:A,pl:unprotect:A} at java.base/java.lang.Thread.run(Thread.java:1583) [?:?] {} [15:43:16] [Server thread/FATAL] [ne.ne.ne.co.NeoForgeMod/]: Preparing crash report with UUID fff0858d-f395-4432-8a0c-13e469be35e1 [15:43:16] [Server thread/ERROR] [minecraft/MinecraftServer]: This crash report has been saved to: /home/runner/work/Archie/Archie/Archie-Test/neoforge/run/./crash-reports/crash-2026-08-08_15.43.16-server.txt [15:43:16] [Server thread/INFO] [minecraft/MinecraftServer]: Stopping server [15:43:16] [Server thread/INFO] [minecraft/MinecraftServer]: Saving players [15:43:16] [Server thread/INFO] [minecraft/MinecraftServer]: Saving worlds [15:43:16] [Server thread/ERROR] [minecraft/MinecraftServer]: Exception stopping the server java.lang.NullPointerException: Cannot invoke "net.minecraft.server.level.ServerLevel.getWorldBorder()" because "serverlevel2" is null at TRANSFORMER/minecraft@1.21.1/net.minecraft.server.MinecraftServer.saveAllChunks(MinecraftServer.java:557) ~[neoforge-21.1.80-minecraft-merged-74f566db8d-1.21.1-loom.mappings.1_21_1.layered+hash.1146786786-v2-neoforge-21.1.80.jar%23179!/:?] {re:mixin,pl:accesstransformer:B,pl:unprotect:A,re:classloading,pl:accesstransformer:B,pl:mixin:APP:archie.mixins.json:threading.ServerMixin from mod archie,pl:mixin:A,pl:unprotect:A} at TRANSFORMER/minecraft@1.21.1/net.minecraft.server.MinecraftServer.stopServer(MinecraftServer.java:622) ~[neoforge-21.1.80-minecraft-merged-74f566db8d-1.21.1-loom.mappings.1_21_1.layered+hash.1146786786-v2-neoforge-21.1.80.jar%23179!/:?] {re:mixin,pl:accesstransformer:B,pl:unprotect:A,re:classloading,pl:accesstransformer:B,pl:mixin:APP:archie.mixins.json:threading.ServerMixin from mod archie,pl:mixin:A,pl:unprotect:A} at TRANSFORMER/minecraft@1.21.1/net.minecraft.client.server.IntegratedServer.stopServer(IntegratedServer.java:221) ~[neoforge-21.1.80-minecraft-merged-74f566db8d-1.21.1-loom.mappings.1_21_1.layered+hash.1146786786-v2-neoforge-21.1.80.jar%23179!/:?] {re:classloading,pl:unprotect:A,pl:runtimedistcleaner:A} at TRANSFORMER/minecraft@1.21.1/net.minecraft.server.MinecraftServer.runServer(MinecraftServer.java:742) ~[neoforge-21.1.80-minecraft-merged-74f566db8d-1.21.1-loom.mappings.1_21_1.layered+hash.1146786786-v2-neoforge-21.1.80.jar%23179!/:?] {re:mixin,pl:accesstransformer:B,pl:unprotect:A,re:classloading,pl:accesstransformer:B,pl:mixin:APP:archie.mixins.json:threading.ServerMixin from mod archie,pl:mixin:A,pl:unprotect:A} at TRANSFORMER/minecraft@1.21.1/net.minecraft.server.MinecraftServer.method_29739(MinecraftServer.java:267) ~[neoforge-21.1.80-minecraft-merged-74f566db8d-1.21.1-loom.mappings.1_21_1.layered+hash.1146786786-v2-neoforge-21.1.80.jar%23179!/:?] {re:mixin,pl:accesstransformer:B,pl:unprotect:A,re:classloading,pl:accesstransformer:B,pl:mixin:APP:archie.mixins.json:threading.ServerMixin from mod archie,pl:mixin:A,pl:unprotect:A} at java.base/java.lang.Thread.run(Thread.java:1583) [?:?] {} [15:43:16] [Server thread/ERROR] [minecraft/MinecraftServer]: Uncaught exception in server thread java.lang.NullPointerException: Cannot invoke "java.util.Map.forEach(java.util.function.BiConsumer)" because "snapshots" is null at TRANSFORMER/neoforge@21.1.80/net.neoforged.neoforge.registries.RegistryManager.applySnapshot(RegistryManager.java:136) ~[neoforge-21.1.80-minecraft-merged-74f566db8d-1.21.1-loom.mappings.1_21_1.layered+hash.1146786786-v2-neoforge-21.1.80.jar%23180!/:?] {re:classloading} at TRANSFORMER/neoforge@21.1.80/net.neoforged.neoforge.registries.RegistryManager.revertToFrozen(RegistryManager.java:120) ~[neoforge-21.1.80-minecraft-merged-74f566db8d-1.21.1-loom.mappings.1_21_1.layered+hash.1146786786-v2-neoforge-21.1.80.jar%23180!/:?] {re:classloading} at TRANSFORMER/neoforge@21.1.80/net.neoforged.neoforge.server.ServerLifecycleHooks.handleServerStopped(ServerLifecycleHooks.java:126) ~[neoforge-21.1.80-minecraft-merged-74f566db8d-1.21.1-loom.mappings.1_21_1.layered+hash.1146786786-v2-neoforge-21.1.80.jar%23180!/:?] {re:classloading} at TRANSFORMER/minecraft@1.21.1/net.minecraft.server.MinecraftServer.runServer(MinecraftServer.java:750) ~[neoforge-21.1.80-minecraft-merged-74f566db8d-1.21.1-loom.mappings.1_21_1.layered+hash.1146786786-v2-neoforge-21.1.80.jar%23179!/:?] {re:mixin,pl:accesstransformer:B,pl:unprotect:A,re:classloading,pl:accesstransformer:B,pl:mixin:APP:archie.mixins.json:threading.ServerMixin from mod archie,pl:mixin:A,pl:unprotect:A} at TRANSFORMER/minecraft@1.21.1/net.minecraft.server.MinecraftServer.method_29739(MinecraftServer.java:267) ~[neoforge-21.1.80-minecraft-merged-74f566db8d-1.21.1-loom.mappings.1_21_1.layered+hash.1146786786-v2-neoforge-21.1.80.jar%23179!/:?] {re:mixin,pl:accesstransformer:B,pl:unprotect:A,re:classloading,pl:accesstransformer:B,pl:mixin:APP:archie.mixins.json:threading.ServerMixin from mod archie,pl:mixin:A,pl:unprotect:A} at java.base/java.lang.Thread.run(Thread.java:1583) [?:?] {} [15:43:26] [Archie Client GameTest Thread/WARN] [ne.ke.ar.Archie/]: Timed out waiting for world load start (testId=archie_test:testscreengametest.testshowcasescreenopensandconfirmdialogroundtrips, timeoutSeconds=300, screen=net.minecraft.client.gui.screens.ProgressScreen@947084119, levelLoaded=false, serverStarted=true) [15:43:46] [Archie Client GameTest Thread/ERROR] [ne.ke.ar.Archie/]: [ClientGameTest] FAIL archie_test:testscreengametest.testshowcasescreenopensandconfirmdialogroundtrips java.lang.reflect.InvocationTargetException: null at java.base/jdk.internal.reflect.DirectMethodHandleAccessor.invoke(DirectMethodHandleAccessor.java:118) ~[?:?] {} at java.base/java.lang.reflect.Method.invoke(Method.java:580) ~[?:?] {re:mixin} at TRANSFORMER/archie@1.0.0/net.kernelpanicsoft.archie.gametest.AClientGameTestHarness.run(AClientGameTestHarness.kt:1561) ~[archie-neoforge-1.0.0-dev.jar%23182!/:?] {re:classloading} at TRANSFORMER/archie@1.0.0/net.kernelpanicsoft.archie.gametest.AGameTestClientHarnessInternal.runIfNeeded$lambda$4(AGameTestClientHarnessInternal.kt:39) ~[archie-neoforge-1.0.0-dev.jar%23182!/:?] {re:mixin,re:classloading} at TRANSFORMER/archie@1.0.0/net.kernelpanicsoft.archie.gametest.ThreadingImpl.runTestThread$lambda$4(ThreadingImpl.kt:104) ~[archie-neoforge-1.0.0-dev.jar%23182!/:?] {re:mixin,re:classloading} at java.base/java.lang.Thread.run(Thread.java:1583) [?:?] {} Caused by: java.lang.IllegalStateException: Timed out waiting for client thread execution (testId=archie_test:testscreengametest.testshowcasescreenopensandconfirmdialogroundtrips, action=run-on-client, timeoutSeconds=10, callerThread=Archie Client GameTest Thread, callerState=RUNNABLE, clientScreen=net.minecraft.client.gui.screens.ProgressScreen@947084119, levelLoaded=false) at TRANSFORMER/archie@1.0.0/net.kernelpanicsoft.archie.gametest.DefaultClientGameTestContext.fail(AClientGameTestHarness.kt:555) ~[archie-neoforge-1.0.0-dev.jar%23182!/:?] {re:classloading} at TRANSFORMER/archie@1.0.0/net.kernelpanicsoft.archie.gametest.DefaultClientGameTestContext.computeOnClient$archie_neoforge(AClientGameTestHarness.kt:611) ~[archie-neoforge-1.0.0-dev.jar%23182!/:?] {re:classloading} at TRANSFORMER/archie@1.0.0/net.kernelpanicsoft.archie.gametest.DefaultClientGameTestContext.runOnClient(AClientGameTestHarness.kt:758) ~[archie-neoforge-1.0.0-dev.jar%23182!/:?] {re:classloading} at TRANSFORMER/archie@1.0.0/net.kernelpanicsoft.archie.gametest.DefaultTestSingleplayerContext.close(AClientGameTestHarness.kt:1029) ~[archie-neoforge-1.0.0-dev.jar%23182!/:?] {re:classloading} at TRANSFORMER/archie@1.0.0/net.kernelpanicsoft.archie.gametest.TestWorldBuilder$DefaultImpls.withSingleplayer(AClientGameTestHarness.kt:213) ~[archie-neoforge-1.0.0-dev.jar%23182!/:?] {re:classloading} at TRANSFORMER/archie@1.0.0/net.kernelpanicsoft.archie.gametest.DefaultTestWorldBuilder.withSingleplayer(AClientGameTestHarness.kt:929) ~[archie-neoforge-1.0.0-dev.jar%23182!/:?] {re:classloading} at TRANSFORMER/archie_test@1.0.0/net.kernelpanicsoft.archie.test.gametest.TestScreenGameTest.testShowcaseScreenOpensAndConfirmDialogRoundTrips$lambda$9(TestScreenGameTest.kt:20) ~[%23181!/:?] {re:classloading} at TRANSFORMER/archie@1.0.0/net.kernelpanicsoft.archie.gametest.ClientGameTestContext$DefaultImpls.withWorld(AClientGameTestHarness.kt:367) ~[archie-neoforge-1.0.0-dev.jar%23182!/:?] {re:classloading} at TRANSFORMER/archie@1.0.0/net.kernelpanicsoft.archie.gametest.DefaultClientGameTestContext.withWorld(AClientGameTestHarness.kt:371) ~[archie-neoforge-1.0.0-dev.jar%23182!/:?] {re:classloading} at TRANSFORMER/archie_test@1.0.0/net.kernelpanicsoft.archie.test.gametest.TestScreenGameTest.testShowcaseScreenOpensAndConfirmDialogRoundTrips(TestScreenGameTest.kt:19) ~[%23181!/:?] {re:classloading} at java.base/jdk.internal.reflect.DirectMethodHandleAccessor.invoke(DirectMethodHandleAccessor.java:103) ~[?:?] {} ... 5 more [15:48:46] [Archie Client GameTest Thread/ERROR] [ne.ke.ar.Archie/]: [ClientGameTest] FAIL archie_test:composeitemcontainermenuclienttests.testexcludedslotbehaviorviarealmenuopen java.lang.reflect.InvocationTargetException: null at java.base/jdk.internal.reflect.DirectMethodHandleAccessor.invoke(DirectMethodHandleAccessor.java:118) ~[?:?] {} at java.base/java.lang.reflect.Method.invoke(Method.java:580) ~[?:?] {re:mixin} at TRANSFORMER/archie@1.0.0/net.kernelpanicsoft.archie.gametest.AClientGameTestHarness.run(AClientGameTestHarness.kt:1561) ~[archie-neoforge-1.0.0-dev.jar%23182!/:?] {re:classloading} at TRANSFORMER/archie@1.0.0/net.kernelpanicsoft.archie.gametest.AGameTestClientHarnessInternal.runIfNeeded$lambda$4(AGameTestClientHarnessInternal.kt:39) ~[archie-neoforge-1.0.0-dev.jar%23182!/:?] {re:mixin,re:classloading} at TRANSFORMER/archie@1.0.0/net.kernelpanicsoft.archie.gametest.ThreadingImpl.runTestThread$lambda$4(ThreadingImpl.kt:104) ~[archie-neoforge-1.0.0-dev.jar%23182!/:?] {re:mixin,re:classloading} at java.base/java.lang.Thread.run(Thread.java:1583) [?:?] {} Caused by: java.lang.IllegalStateException: Timed out waiting for client thread execution (testId=archie_test:composeitemcontainermenuclienttests.testexcludedslotbehaviorviarealmenuopen, action=world-builder-open-create-screen, timeoutSeconds=300, callerThread=Archie Client GameTest Thread, callerState=RUNNABLE, clientScreen=net.minecraft.client.gui.screens.ProgressScreen@947084119, levelLoaded=false) at TRANSFORMER/archie@1.0.0/net.kernelpanicsoft.archie.gametest.DefaultClientGameTestContext.fail(AClientGameTestHarness.kt:555) ~[archie-neoforge-1.0.0-dev.jar%23182!/:?] {re:classloading} at TRANSFORMER/archie@1.0.0/net.kernelpanicsoft.archie.gametest.DefaultClientGameTestContext.computeOnClient$archie_neoforge(AClientGameTestHarness.kt:611) ~[archie-neoforge-1.0.0-dev.jar%23182!/:?] {re:classloading} at TRANSFORMER/archie@1.0.0/net.kernelpanicsoft.archie.gametest.DefaultTestWorldBuilder.create(AClientGameTestHarness.kt:944) ~[archie-neoforge-1.0.0-dev.jar%23182!/:?] {re:classloading} at TRANSFORMER/archie@1.0.0/net.kernelpanicsoft.archie.gametest.TestWorldBuilder$DefaultImpls.withSingleplayer(AClientGameTestHarness.kt:209) ~[archie-neoforge-1.0.0-dev.jar%23182!/:?] {re:classloading} at TRANSFORMER/archie@1.0.0/net.kernelpanicsoft.archie.gametest.DefaultTestWorldBuilder.withSingleplayer(AClientGameTestHarness.kt:929) ~[archie-neoforge-1.0.0-dev.jar%23182!/:?] {re:classloading} at TRANSFORMER/archie_test@1.0.0/net.kernelpanicsoft.archie.test.gametest.ComposeItemContainerMenuClientTests.testExcludedSlotBehaviorViaRealMenuOpen$lambda$6(ComposeItemContainerMenuClientTests.kt:31) ~[%23181!/:?] {re:classloading} at TRANSFORMER/archie@1.0.0/net.kernelpanicsoft.archie.gametest.ClientGameTestContext$DefaultImpls.withWorld(AClientGameTestHarness.kt:367) ~[archie-neoforge-1.0.0-dev.jar%23182!/:?] {re:classloading} at TRANSFORMER/archie@1.0.0/net.kernelpanicsoft.archie.gametest.DefaultClientGameTestContext.withWorld(AClientGameTestHarness.kt:371) ~[archie-neoforge-1.0.0-dev.jar%23182!/:?] {re:classloading} at TRANSFORMER/archie_test@1.0.0/net.kernelpanicsoft.archie.test.gametest.ComposeItemContainerMenuClientTests.testExcludedSlotBehaviorViaRealMenuOpen(ComposeItemContainerMenuClientTests.kt:30) ~[%23181!/:?] {re:classloading} at java.base/jdk.internal.reflect.DirectMethodHandleAccessor.invoke(DirectMethodHandleAccessor.java:103) ~[?:?] {} ... 5 more [15:48:46] [Archie Client GameTest Thread/INFO] [ne.ke.ar.Archie/]: [ClientGameTest] Completed: passed=0, failed=2, skipped=0 [15:48:46] [Archie Client GameTest Thread/ERROR] [ne.ke.ar.Archie/]: Client GameTests failed: 2 failing test(s) - archie_test:testscreengametest.testshowcasescreenopensandconfirmdialogroundtrips: java.lang.IllegalStateException: Timed out waiting for client thread execution (testId=archie_test:testscreengametest.testshowcasescreenopensandconfirmdialogroundtrips, action=run-on-client, timeoutSeconds=10, callerThread=Archie Client GameTest Thread, callerState=RUNNABLE, clientScreen=net.minecraft.client.gui.screens.ProgressScreen@947084119, levelLoaded=false) - archie_test:composeitemcontainermenuclienttests.testexcludedslotbehaviorviarealmenuopen: java.lang.IllegalStateException: Timed out waiting for client thread execution (testId=archie_test:composeitemcontainermenuclienttests.testexcludedslotbehaviorviarealmenuopen, action=world-builder-open-create-screen, timeoutSeconds=300, callerThread=Archie Client GameTest Thread, callerState=RUNNABLE, clientScreen=net.minecraft.client.gui.screens.ProgressScreen@947084119, levelLoaded=false) > Task :neoforge-test:runGametestClient FAILED gradle/actions: Writing build results to /home/runner/work/_temp/.gradle-actions/build-results/__run_2-1786203741060.json FAILURE: Build failed with an exception. * What went wrong: Execution failed for task ':neoforge-test:runGametestClient'. > Process 'command '/opt/hostedtoolcache/Java_Zulu_jdk/21.0.12-8/x64/bin/java'' finished with non-zero exit value 1 * Try: > Run with --stacktrace option to get the stack trace. > Run with --info or --debug option to get more log output. > Run with --scan to get full insights. > Get more help at https://help.gradle.org. BUILD FAILED in 6m 33s 32 actionable tasks: 9 executed, 23 up-to-date
Raw output
java.lang.AssertionError: GameTest failed: testExcludedSlotBehaviorViaRealMenuOpen [neoforge:client]
Exit code: 1
Command: /home/runner/work/Archie/Archie/Archie-Test/gradlew :neoforge:runGametestClient --console=plain --no-daemon
Log file: /home/runner/work/Archie/Archie/Archie-Test/build/tmp/junit-gametest-runner/neoforge-client.log
--- Log tail ---
	at TRANSFORMER/minecraft@1.21.1/net.minecraft.client.Minecraft.runTick(Minecraft.java:1155) ~[neoforge-21.1.80-minecraft-merged-74f566db8d-1.21.1-loom.mappings.1_21_1.layered+hash.1146786786-v2-neoforge-21.1.80.jar%23179!/:?] {re:mixin,pl:unprotect:A,pl:runtimedistcleaner:A,re:classloading,pl:mixin:A,pl:unprotect:A,pl:runtimedistcleaner:A}
	at TRANSFORMER/minecraft@1.21.1/net.minecraft.client.Minecraft.run(Minecraft.java:807) ~[neoforge-21.1.80-minecraft-merged-74f566db8d-1.21.1-loom.mappings.1_21_1.layered+hash.1146786786-v2-neoforge-21.1.80.jar%23179!/:?] {re:mixin,pl:unprotect:A,pl:runtimedistcleaner:A,re:classloading,pl:mixin:A,pl:unprotect:A,pl:runtimedistcleaner:A}
	at TRANSFORMER/minecraft@1.21.1/net.minecraft.client.main.Main.main(Main.java:230) ~[neoforge-21.1.80-minecraft-merged-74f566db8d-1.21.1-loom.mappings.1_21_1.layered+hash.1146786786-v2-neoforge-21.1.80.jar%23179!/:?] {re:classloading,pl:unprotect:A,pl:runtimedistcleaner:A}
	at java.base/jdk.internal.reflect.DirectMethodHandleAccessor.invoke(DirectMethodHandleAccessor.java:103) ~[?:?] {}
	at java.base/java.lang.reflect.Method.invoke(Method.java:580) ~[?:?] {re:mixin}
	at MC-BOOTSTRAP/fml_loader@4.0.31/net.neoforged.fml.loading.targets.CommonLaunchHandler.runTarget(CommonLaunchHandler.java:136) ~[loader-4.0.31.jar%2390!/:4.0] {}
	at MC-BOOTSTRAP/fml_loader@4.0.31/net.neoforged.fml.loading.targets.CommonLaunchHandler.clientService(CommonLaunchHandler.java:124) ~[loader-4.0.31.jar%2390!/:4.0] {}
	at MC-BOOTSTRAP/fml_loader@4.0.31/net.neoforged.fml.loading.targets.NeoForgeClientUserdevLaunchHandler.runService(NeoForgeClientUserdevLaunchHandler.java:23) ~[loader-4.0.31.jar%2390!/:4.0] {}
	at MC-BOOTSTRAP/fml_loader@4.0.31/net.neoforged.fml.loading.targets.CommonLaunchHandler.lambda$launchService$4(CommonLaunchHandler.java:118) ~[loader-4.0.31.jar%2390!/:4.0] {}
	at MC-BOOTSTRAP/cpw.mods.modlauncher@11.0.4/cpw.mods.modlauncher.LaunchServiceHandlerDecorator.launch(LaunchServiceHandlerDecorator.java:30) [modlauncher-11.0.4.jar%2392!/:?] {}
	at MC-BOOTSTRAP/cpw.mods.modlauncher@11.0.4/cpw.mods.modlauncher.LaunchServiceHandler.launch(LaunchServiceHandler.java:53) [modlauncher-11.0.4.jar%2392!/:?] {}
	at MC-BOOTSTRAP/cpw.mods.modlauncher@11.0.4/cpw.mods.modlauncher.LaunchServiceHandler.launch(LaunchServiceHandler.java:71) [modlauncher-11.0.4.jar%2392!/:?] {}
	at MC-BOOTSTRAP/cpw.mods.modlauncher@11.0.4/cpw.mods.modlauncher.Launcher.run(Launcher.java:103) [modlauncher-11.0.4.jar%2392!/:?] {}
	at MC-BOOTSTRAP/cpw.mods.modlauncher@11.0.4/cpw.mods.modlauncher.Launcher.main(Launcher.java:74) [modlauncher-11.0.4.jar%2392!/:?] {}
	at MC-BOOTSTRAP/cpw.mods.modlauncher@11.0.4/cpw.mods.modlauncher.BootstrapLaunchConsumer.accept(BootstrapLaunchConsumer.java:26) [modlauncher-11.0.4.jar%2392!/:?] {}
	at MC-BOOTSTRAP/cpw.mods.modlauncher@11.0.4/cpw.mods.modlauncher.BootstrapLaunchConsumer.accept(BootstrapLaunchConsumer.java:23) [modlauncher-11.0.4.jar%2392!/:?] {}
	at cpw.mods.bootstraplauncher.BootstrapLauncher.run(BootstrapLauncher.java:210) [bootstraplauncher-2.0.2.jar:?] {}
	at cpw.mods.bootstraplauncher.BootstrapLauncher.main(BootstrapLauncher.java:69) [bootstraplauncher-2.0.2.jar:?] {}
	at net.fabricmc.devlaunchinjector.Main.main(Main.java:86) [dev-launch-injector-0.2.1+build.8.jar:?] {}
[15:43:14] [Render thread/INFO] [minecraft/RecipeManager]: Loaded 1277 recipes
[15:43:15] [Render thread/INFO] [minecraft/AdvancementTree]: Loaded 1399 advancements
[15:43:15] [Render thread/ERROR] [ne.ne.fm.ModLoader/]: Cowardly refusing to send event net.neoforged.neoforge.event.AddPackFindersEvent to a broken mod state
[15:43:15] [Server thread/INFO] [minecraft/IntegratedServer]: Starting integrated minecraft server version 1.21.1
[15:43:15] [Server thread/INFO] [minecraft/MinecraftServer]: Generating keypair
[15:43:15] [Server thread/ERROR] [minecraft/MinecraftServer]: Encountered an unexpected exception
java.lang.IllegalStateException: Missing registry: ResourceKey[minecraft:root / neoforge:biome_modifier]
	at TRANSFORMER/minecraft@1.21.1/net.minecraft.core.RegistryAccess.method_30534(RegistryAccess.java:26) ~[neoforge-21.1.80-minecraft-merged-74f566db8d-1.21.1-loom.mappings.1_21_1.layered+hash.1146786786-v2-neoforge-21.1.80.jar%23179!/:?] {re:mixin,pl:unprotect:A,re:classloading,pl:unprotect:A}
	at java.base/java.util.Optional.orElseThrow(Optional.java:403) ~[?:?] {re:computing_frames,re:mixin}
	at TRANSFORMER/minecraft@1.21.1/net.minecraft.core.RegistryAccess.registryOrThrow(RegistryAccess.java:26) ~[neoforge-21.1.80-minecraft-merged-74f566db8d-1.21.1-loom.mappings.1_21_1.layered+hash.1146786786-v2-neoforge-21.1.80.jar%23179!/:?] {re:mixin,pl:unprotect:A,re:classloading,pl:unprotect:A}
	at TRANSFORMER/neoforge@21.1.80/net.neoforged.neoforge.server.ServerLifecycleHooks.runModifiers(ServerLifecycleHooks.java:164) ~[neoforge-21.1.80-minecraft-merged-74f566db8d-1.21.1-loom.mappings.1_21_1.layered+hash.1146786786-v2-neoforge-21.1.80.jar%23180!/:?] {re:classloading}
	at TRANSFORMER/neoforge@21.1.80/net.neoforged.neoforge.server.ServerLifecycleHooks.handleServerAboutToStart(ServerLifecycleHooks.java:98) ~[neoforge-21.1.80-minecraft-merged-74f566db8d-1.21.1-loom.mappings.1_21_1.layered+hash.1146786786-v2-neoforge-21.1.80.jar%23180!/:?] {re:classloading}
	at TRANSFORMER/minecraft@1.21.1/net.minecraft.client.server.IntegratedServer.initServer(IntegratedServer.java:76) ~[neoforge-21.1.80-minecraft-merged-74f566db8d-1.21.1-loom.mappings.1_21_1.layered+hash.1146786786-v2-neoforge-21.1.80.jar%23179!/:?] {re:classloading,pl:unprotect:A,pl:runtimedistcleaner:A}
	at TRANSFORMER/minecraft@1.21.1/net.minecraft.server.MinecraftServer.runServer(MinecraftServer.java:670) ~[neoforge-21.1.80-minecraft-merged-74f566db8d-1.21.1-loom.mappings.1_21_1.layered+hash.1146786786-v2-neoforge-21.1.80.jar%23179!/:?] {re:mixin,pl:accesstransformer:B,pl:unprotect:A,re:classloading,pl:accesstransformer:B,pl:mixin:APP:archie.mixins.json:threading.ServerMixin from mod archie,pl:mixin:A,pl:unprotect:A}
	at TRANSFORMER/minecraft@1.21.1/net.minecraft.server.MinecraftServer.method_29739(MinecraftServer.java:267) ~[neoforge-21.1.80-minecraft-merged-74f566db8d-1.21.1-loom.mappings.1_21_1.layered+hash.1146786786-v2-neoforge-21.1.80.jar%23179!/:?] {re:mixin,pl:accesstransformer:B,pl:unprotect:A,re:classloading,pl:accesstransformer:B,pl:mixin:APP:archie.mixins.json:threading.ServerMixin from mod archie,pl:mixin:A,pl:unprotect:A}
	at java.base/java.lang.Thread.run(Thread.java:1583) [?:?] {}
[15:43:16] [Server thread/FATAL] [ne.ne.ne.co.NeoForgeMod/]: Preparing crash report with UUID fff0858d-f395-4432-8a0c-13e469be35e1
[15:43:16] [Server thread/ERROR] [minecraft/MinecraftServer]: This crash report has been saved to: /home/runner/work/Archie/Archie/Archie-Test/neoforge/run/./crash-reports/crash-2026-08-08_15.43.16-server.txt
[15:43:16] [Server thread/INFO] [minecraft/MinecraftServer]: Stopping server
[15:43:16] [Server thread/INFO] [minecraft/MinecraftServer]: Saving players
[15:43:16] [Server thread/INFO] [minecraft/MinecraftServer]: Saving worlds
[15:43:16] [Server thread/ERROR] [minecraft/MinecraftServer]: Exception stopping the server
java.lang.NullPointerException: Cannot invoke "net.minecraft.server.level.ServerLevel.getWorldBorder()" because "serverlevel2" is null
	at TRANSFORMER/minecraft@1.21.1/net.minecraft.server.MinecraftServer.saveAllChunks(MinecraftServer.java:557) ~[neoforge-21.1.80-minecraft-merged-74f566db8d-1.21.1-loom.mappings.1_21_1.layered+hash.1146786786-v2-neoforge-21.1.80.jar%23179!/:?] {re:mixin,pl:accesstransformer:B,pl:unprotect:A,re:classloading,pl:accesstransformer:B,pl:mixin:APP:archie.mixins.json:threading.ServerMixin from mod archie,pl:mixin:A,pl:unprotect:A}
	at TRANSFORMER/minecraft@1.21.1/net.minecraft.server.MinecraftServer.stopServer(MinecraftServer.java:622) ~[neoforge-21.1.80-minecraft-merged-74f566db8d-1.21.1-loom.mappings.1_21_1.layered+hash.1146786786-v2-neoforge-21.1.80.jar%23179!/:?] {re:mixin,pl:accesstransformer:B,pl:unprotect:A,re:classloading,pl:accesstransformer:B,pl:mixin:APP:archie.mixins.json:threading.ServerMixin from mod archie,pl:mixin:A,pl:unprotect:A}
	at TRANSFORMER/minecraft@1.21.1/net.minecraft.client.server.IntegratedServer.stopServer(IntegratedServer.java:221) ~[neoforge-21.1.80-minecraft-merged-74f566db8d-1.21.1-loom.mappings.1_21_1.layered+hash.1146786786-v2-neoforge-21.1.80.jar%23179!/:?] {re:classloading,pl:unprotect:A,pl:runtimedistcleaner:A}
	at TRANSFORMER/minecraft@1.21.1/net.minecraft.server.MinecraftServer.runServer(MinecraftServer.java:742) ~[neoforge-21.1.80-minecraft-merged-74f566db8d-1.21.1-loom.mappings.1_21_1.layered+hash.1146786786-v2-neoforge-21.1.80.jar%23179!/:?] {re:mixin,pl:accesstransformer:B,pl:unprotect:A,re:classloading,pl:accesstransformer:B,pl:mixin:APP:archie.mixins.json:threading.ServerMixin from mod archie,pl:mixin:A,pl:unprotect:A}
	at TRANSFORMER/minecraft@1.21.1/net.minecraft.server.MinecraftServer.method_29739(MinecraftServer.java:267) ~[neoforge-21.1.80-minecraft-merged-74f566db8d-1.21.1-loom.mappings.1_21_1.layered+hash.1146786786-v2-neoforge-21.1.80.jar%23179!/:?] {re:mixin,pl:accesstransformer:B,pl:unprotect:A,re:classloading,pl:accesstransformer:B,pl:mixin:APP:archie.mixins.json:threading.ServerMixin from mod archie,pl:mixin:A,pl:unprotect:A}
	at java.base/java.lang.Thread.run(Thread.java:1583) [?:?] {}
[15:43:16] [Server thread/ERROR] [minecraft/MinecraftServer]: Uncaught exception in server thread
java.lang.NullPointerException: Cannot invoke "java.util.Map.forEach(java.util.function.BiConsumer)" because "snapshots" is null
	at TRANSFORMER/neoforge@21.1.80/net.neoforged.neoforge.registries.RegistryManager.applySnapshot(RegistryManager.java:136) ~[neoforge-21.1.80-minecraft-merged-74f566db8d-1.21.1-loom.mappings.1_21_1.layered+hash.1146786786-v2-neoforge-21.1.80.jar%23180!/:?] {re:classloading}
	at TRANSFORMER/neoforge@21.1.80/net.neoforged.neoforge.registries.RegistryManager.revertToFrozen(RegistryManager.java:120) ~[neoforge-21.1.80-minecraft-merged-74f566db8d-1.21.1-loom.mappings.1_21_1.layered+hash.1146786786-v2-neoforge-21.1.80.jar%23180!/:?] {re:classloading}
	at TRANSFORMER/neoforge@21.1.80/net.neoforged.neoforge.server.ServerLifecycleHooks.handleServerStopped(ServerLifecycleHooks.java:126) ~[neoforge-21.1.80-minecraft-merged-74f566db8d-1.21.1-loom.mappings.1_21_1.layered+hash.1146786786-v2-neoforge-21.1.80.jar%23180!/:?] {re:classloading}
	at TRANSFORMER/minecraft@1.21.1/net.minecraft.server.MinecraftServer.runServer(MinecraftServer.java:750) ~[neoforge-21.1.80-minecraft-merged-74f566db8d-1.21.1-loom.mappings.1_21_1.layered+hash.1146786786-v2-neoforge-21.1.80.jar%23179!/:?] {re:mixin,pl:accesstransformer:B,pl:unprotect:A,re:classloading,pl:accesstransformer:B,pl:mixin:APP:archie.mixins.json:threading.ServerMixin from mod archie,pl:mixin:A,pl:unprotect:A}
	at TRANSFORMER/minecraft@1.21.1/net.minecraft.server.MinecraftServer.method_29739(MinecraftServer.java:267) ~[neoforge-21.1.80-minecraft-merged-74f566db8d-1.21.1-loom.mappings.1_21_1.layered+hash.1146786786-v2-neoforge-21.1.80.jar%23179!/:?] {re:mixin,pl:accesstransformer:B,pl:unprotect:A,re:classloading,pl:accesstransformer:B,pl:mixin:APP:archie.mixins.json:threading.ServerMixin from mod archie,pl:mixin:A,pl:unprotect:A}
	at java.base/java.lang.Thread.run(Thread.java:1583) [?:?] {}
[15:43:26] [Archie Client GameTest Thread/WARN] [ne.ke.ar.Archie/]: Timed out waiting for world load start (testId=archie_test:testscreengametest.testshowcasescreenopensandconfirmdialogroundtrips, timeoutSeconds=300, screen=net.minecraft.client.gui.screens.ProgressScreen@947084119, levelLoaded=false, serverStarted=true)
[15:43:46] [Archie Client GameTest Thread/ERROR] [ne.ke.ar.Archie/]: [ClientGameTest] FAIL archie_test:testscreengametest.testshowcasescreenopensandconfirmdialogroundtrips
java.lang.reflect.InvocationTargetException: null
	at java.base/jdk.internal.reflect.DirectMethodHandleAccessor.invoke(DirectMethodHandleAccessor.java:118) ~[?:?] {}
	at java.base/java.lang.reflect.Method.invoke(Method.java:580) ~[?:?] {re:mixin}
	at TRANSFORMER/archie@1.0.0/net.kernelpanicsoft.archie.gametest.AClientGameTestHarness.run(AClientGameTestHarness.kt:1561) ~[archie-neoforge-1.0.0-dev.jar%23182!/:?] {re:classloading}
	at TRANSFORMER/archie@1.0.0/net.kernelpanicsoft.archie.gametest.AGameTestClientHarnessInternal.runIfNeeded$lambda$4(AGameTestClientHarnessInternal.kt:39) ~[archie-neoforge-1.0.0-dev.jar%23182!/:?] {re:mixin,re:classloading}
	at TRANSFORMER/archie@1.0.0/net.kernelpanicsoft.archie.gametest.ThreadingImpl.runTestThread$lambda$4(ThreadingImpl.kt:104) ~[archie-neoforge-1.0.0-dev.jar%23182!/:?] {re:mixin,re:classloading}
	at java.base/java.lang.Thread.run(Thread.java:1583) [?:?] {}
Caused by: java.lang.IllegalStateException: Timed out waiting for client thread execution (testId=archie_test:testscreengametest.testshowcasescreenopensandconfirmdialogroundtrips, action=run-on-client, timeoutSeconds=10, callerThread=Archie Client GameTest Thread, callerState=RUNNABLE, clientScreen=net.minecraft.client.gui.screens.ProgressScreen@947084119, levelLoaded=false)
	at TRANSFORMER/archie@1.0.0/net.kernelpanicsoft.archie.gametest.DefaultClientGameTestContext.fail(AClientGameTestHarness.kt:555) ~[archie-neoforge-1.0.0-dev.jar%23182!/:?] {re:classloading}
	at TRANSFORMER/archie@1.0.0/net.kernelpanicsoft.archie.gametest.DefaultClientGameTestContext.computeOnClient$archie_neoforge(AClientGameTestHarness.kt:611) ~[archie-neoforge-1.0.0-dev.jar%23182!/:?] {re:classloading}
	at TRANSFORMER/archie@1.0.0/net.kernelpanicsoft.archie.gametest.DefaultClientGameTestContext.runOnClient(AClientGameTestHarness.kt:758) ~[archie-neoforge-1.0.0-dev.jar%23182!/:?] {re:classloading}
	at TRANSFORMER/archie@1.0.0/net.kernelpanicsoft.archie.gametest.DefaultTestSingleplayerContext.close(AClientGameTestHarness.kt:1029) ~[archie-neoforge-1.0.0-dev.jar%23182!/:?] {re:classloading}
	at TRANSFORMER/archie@1.0.0/net.kernelpanicsoft.archie.gametest.TestWorldBuilder$DefaultImpls.withSingleplayer(AClientGameTestHarness.kt:213) ~[archie-neoforge-1.0.0-dev.jar%23182!/:?] {re:classloading}
	at TRANSFORMER/archie@1.0.0/net.kernelpanicsoft.archie.gametest.DefaultTestWorldBuilder.withSingleplayer(AClientGameTestHarness.kt:929) ~[archie-neoforge-1.0.0-dev.jar%23182!/:?] {re:classloading}
	at TRANSFORMER/archie_test@1.0.0/net.kernelpanicsoft.archie.test.gametest.TestScreenGameTest.testShowcaseScreenOpensAndConfirmDialogRoundTrips$lambda$9(TestScreenGameTest.kt:20) ~[%23181!/:?] {re:classloading}
	at TRANSFORMER/archie@1.0.0/net.kernelpanicsoft.archie.gametest.ClientGameTestContext$DefaultImpls.withWorld(AClientGameTestHarness.kt:367) ~[archie-neoforge-1.0.0-dev.jar%23182!/:?] {re:classloading}
	at TRANSFORMER/archie@1.0.0/net.kernelpanicsoft.archie.gametest.DefaultClientGameTestContext.withWorld(AClientGameTestHarness.kt:371) ~[archie-neoforge-1.0.0-dev.jar%23182!/:?] {re:classloading}
	at TRANSFORMER/archie_test@1.0.0/net.kernelpanicsoft.archie.test.gametest.TestScreenGameTest.testShowcaseScreenOpensAndConfirmDialogRoundTrips(TestScreenGameTest.kt:19) ~[%23181!/:?] {re:classloading}
	at java.base/jdk.internal.reflect.DirectMethodHandleAccessor.invoke(DirectMethodHandleAccessor.java:103) ~[?:?] {}
	... 5 more
[15:48:46] [Archie Client GameTest Thread/ERROR] [ne.ke.ar.Archie/]: [ClientGameTest] FAIL archie_test:composeitemcontainermenuclienttests.testexcludedslotbehaviorviarealmenuopen
java.lang.reflect.InvocationTargetException: null
	at java.base/jdk.internal.reflect.DirectMethodHandleAccessor.invoke(DirectMethodHandleAccessor.java:118) ~[?:?] {}
	at java.base/java.lang.reflect.Method.invoke(Method.java:580) ~[?:?] {re:mixin}
	at TRANSFORMER/archie@1.0.0/net.kernelpanicsoft.archie.gametest.AClientGameTestHarness.run(AClientGameTestHarness.kt:1561) ~[archie-neoforge-1.0.0-dev.jar%23182!/:?] {re:classloading}
	at TRANSFORMER/archie@1.0.0/net.kernelpanicsoft.archie.gametest.AGameTestClientHarnessInternal.runIfNeeded$lambda$4(AGameTestClientHarnessInternal.kt:39) ~[archie-neoforge-1.0.0-dev.jar%23182!/:?] {re:mixin,re:classloading}
	at TRANSFORMER/archie@1.0.0/net.kernelpanicsoft.archie.gametest.ThreadingImpl.runTestThread$lambda$4(ThreadingImpl.kt:104) ~[archie-neoforge-1.0.0-dev.jar%23182!/:?] {re:mixin,re:classloading}
	at java.base/java.lang.Thread.run(Thread.java:1583) [?:?] {}
Caused by: java.lang.IllegalStateException: Timed out waiting for client thread execution (testId=archie_test:composeitemcontainermenuclienttests.testexcludedslotbehaviorviarealmenuopen, action=world-builder-open-create-screen, timeoutSeconds=300, callerThread=Archie Client GameTest Thread, callerState=RUNNABLE, clientScreen=net.minecraft.client.gui.screens.ProgressScreen@947084119, levelLoaded=false)
	at TRANSFORMER/archie@1.0.0/net.kernelpanicsoft.archie.gametest.DefaultClientGameTestContext.fail(AClientGameTestHarness.kt:555) ~[archie-neoforge-1.0.0-dev.jar%23182!/:?] {re:classloading}
	at TRANSFORMER/archie@1.0.0/net.kernelpanicsoft.archie.gametest.DefaultClientGameTestContext.computeOnClient$archie_neoforge(AClientGameTestHarness.kt:611) ~[archie-neoforge-1.0.0-dev.jar%23182!/:?] {re:classloading}
	at TRANSFORMER/archie@1.0.0/net.kernelpanicsoft.archie.gametest.DefaultTestWorldBuilder.create(AClientGameTestHarness.kt:944) ~[archie-neoforge-1.0.0-dev.jar%23182!/:?] {re:classloading}
	at TRANSFORMER/archie@1.0.0/net.kernelpanicsoft.archie.gametest.TestWorldBuilder$DefaultImpls.withSingleplayer(AClientGameTestHarness.kt:209) ~[archie-neoforge-1.0.0-dev.jar%23182!/:?] {re:classloading}
	at TRANSFORMER/archie@1.0.0/net.kernelpanicsoft.archie.gametest.DefaultTestWorldBuilder.withSingleplayer(AClientGameTestHarness.kt:929) ~[archie-neoforge-1.0.0-dev.jar%23182!/:?] {re:classloading}
	at TRANSFORMER/archie_test@1.0.0/net.kernelpanicsoft.archie.test.gametest.ComposeItemContainerMenuClientTests.testExcludedSlotBehaviorViaRealMenuOpen$lambda$6(ComposeItemContainerMenuClientTests.kt:31) ~[%23181!/:?] {re:classloading}
	at TRANSFORMER/archie@1.0.0/net.kernelpanicsoft.archie.gametest.ClientGameTestContext$DefaultImpls.withWorld(AClientGameTestHarness.kt:367) ~[archie-neoforge-1.0.0-dev.jar%23182!/:?] {re:classloading}
	at TRANSFORMER/archie@1.0.0/net.kernelpanicsoft.archie.gametest.DefaultClientGameTestContext.withWorld(AClientGameTestHarness.kt:371) ~[archie-neoforge-1.0.0-dev.jar%23182!/:?] {re:classloading}
	at TRANSFORMER/archie_test@1.0.0/net.kernelpanicsoft.archie.test.gametest.ComposeItemContainerMenuClientTests.testExcludedSlotBehaviorViaRealMenuOpen(ComposeItemContainerMenuClientTests.kt:30) ~[%23181!/:?] {re:classloading}
	at java.base/jdk.internal.reflect.DirectMethodHandleAccessor.invoke(DirectMethodHandleAccessor.java:103) ~[?:?] {}
	... 5 more
[15:48:46] [Archie Client GameTest Thread/INFO] [ne.ke.ar.Archie/]: [ClientGameTest] Completed: passed=0, failed=2, skipped=0
[15:48:46] [Archie Client GameTest Thread/ERROR] [ne.ke.ar.Archie/]: Client GameTests failed: 2 failing test(s)
 - archie_test:testscreengametest.testshowcasescreenopensandconfirmdialogroundtrips: java.lang.IllegalStateException: Timed out waiting for client thread execution (testId=archie_test:testscreengametest.testshowcasescreenopensandconfirmdialogroundtrips, action=run-on-client, timeoutSeconds=10, callerThread=Archie Client GameTest Thread, callerState=RUNNABLE, clientScreen=net.minecraft.client.gui.screens.ProgressScreen@947084119, levelLoaded=false)
 - archie_test:composeitemcontainermenuclienttests.testexcludedslotbehaviorviarealmenuopen: java.lang.IllegalStateException: Timed out waiting for client thread execution (testId=archie_test:composeitemcontainermenuclienttests.testexcludedslotbehaviorviarealmenuopen, action=world-builder-open-create-screen, timeoutSeconds=300, callerThread=Archie Client GameTest Thread, callerState=RUNNABLE, clientScreen=net.minecraft.client.gui.screens.ProgressScreen@947084119, levelLoaded=false)

> Task :neoforge-test:runGametestClient FAILED
gradle/actions: Writing build results to /home/runner/work/_temp/.gradle-actions/build-results/__run_2-1786203741060.json

FAILURE: Build failed with an exception.

* What went wrong:
Execution failed for task ':neoforge-test:runGametestClient'.
> Process 'command '/opt/hostedtoolcache/Java_Zulu_jdk/21.0.12-8/x64/bin/java'' finished with non-zero exit value 1

* Try:
> Run with --stacktrace option to get the stack trace.
> Run with --info or --debug option to get more log output.
> Run with --scan to get full insights.
> Get more help at https://help.gradle.org.

BUILD FAILED in 6m 33s
32 actionable tasks: 9 executed, 23 up-to-date
	at net.kernelpanicsoft.archie.gametest.junit.GameTestRunner.tests$lambda$13$lambda$12$lambda$11$lambda$10$lambda$9(GameTestRunner.kt:116)
	at org.junit.jupiter.engine.descriptor.DynamicTestTestDescriptor.lambda$execute$0(DynamicTestTestDescriptor.java:53)
	at org.junit.jupiter.engine.execution.InvocationInterceptorChain$ValidatingInvocation.proceed(InvocationInterceptorChain.java:131)
	at org.junit.jupiter.api.extension.InvocationInterceptor.interceptDynamicTest(InvocationInterceptor.java:167)
	at org.junit.jupiter.api.extension.InvocationInterceptor.interceptDynamicTest(InvocationInterceptor.java:184)
	at org.junit.jupiter.engine.descriptor.DynamicTestTestDescriptor.lambda$execute$1(DynamicTestTestDescriptor.java:61)
	at org.junit.jupiter.engine.execution.InvocationInterceptorChain$InterceptorCall.lambda$ofVoid$0(InvocationInterceptorChain.java:78)
	at org.junit.jupiter.engine.execution.InvocationInterceptorChain$InterceptedInvocation.proceed(InvocationInterceptorChain.java:106)
	at org.junit.jupiter.engine.execution.InvocationInterceptorChain.proceed(InvocationInterceptorChain.java:64)
	at org.junit.jupiter.engine.execution.InvocationInterceptorChain.chainAndInvoke(InvocationInterceptorChain.java:45)
	at org.junit.jupiter.engine.execution.InvocationInterceptorChain.invoke(InvocationInterceptorChain.java:37)
	at org.junit.jupiter.engine.descriptor.DynamicTestTestDescriptor.execute(DynamicTestTestDescriptor.java:60)
	at org.junit.jupiter.engine.descriptor.DynamicTestTestDescriptor.execute(DynamicTestTestDescriptor.java:32)
	at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$6(NodeTestTask.java:156)
	at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
	at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$8(NodeTestTask.java:146)
	at org.junit.platform.engine.support.hierarchical.Node.around(Node.java:137)
	at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$9(NodeTestTask.java:144)
	at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
	at org.junit.platform.engine.support.hierarchical.NodeTestTask.executeRecursively(NodeTestTask.java:143)
	at org.junit.platform.engine.support.hierarchical.NodeTestTask.execute(NodeTestTask.java:100)
	at org.junit.platform.engine.support.hierarchical.ForkJoinPoolHierarchicalTestExecutorService$ExclusiveTask.exec(ForkJoinPoolHierarchicalTestExecutorService.java:274)
	at java.base/java.util.concurrent.ForkJoinTask.doExec(ForkJoinTask.java:387)
	at java.base/java.util.concurrent.ForkJoinPool$WorkQueue.tryRemoveAndExec(ForkJoinPool.java:1351)
	at java.base/java.util.concurrent.ForkJoinTask.awaitDone(ForkJoinTask.java:422)
	at java.base/java.util.concurrent.ForkJoinTask.get(ForkJoinTask.java:991)
	at org.junit.platform.engine.support.hierarchical.NodeTestTask$DefaultDynamicTestExecutor.awaitFinished(NodeTestTask.java:241)
	at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
	at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$6(NodeTestTask.java:163)
	at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
	at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$8(NodeTestTask.java:146)
	at org.junit.platform.engine.support.hierarchical.Node.around(Node.java:137)
	at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$9(NodeTestTask.java:144)
	at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
	at org.junit.platform.engine.support.hierarchical.NodeTestTask.executeRecursively(NodeTestTask.java:143)
	at org.junit.platform.engine.support.hierarchical.NodeTestTask.execute(NodeTestTask.java:100)
	at org.junit.platform.engine.support.hierarchical.ForkJoinPoolHierarchicalTestExecutorService$ExclusiveTask.exec(ForkJoinPoolHierarchicalTestExecutorService.java:274)
	at java.base/java.util.concurrent.ForkJoinTask.doExec(ForkJoinTask.java:387)
	at java.base/java.util.concurrent.ForkJoinPool$WorkQueue.tryRemoveAndExec(ForkJoinPool.java:1351)
	at java.base/java.util.concurrent.ForkJoinTask.awaitDone(ForkJoinTask.java:422)
	at java.base/java.util.concurrent.ForkJoinTask.get(ForkJoinTask.java:991)
	at org.junit.platform.engine.support.hierarchical.NodeTestTask$DefaultDynamicTestExecutor.awaitFinished(NodeTestTask.java:241)
	at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
	at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$6(NodeTestTask.java:163)
	at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
	at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$8(NodeTestTask.java:146)
	at org.junit.platform.engine.support.hierarchical.Node.around(Node.java:137)
	at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$9(NodeTestTask.java:144)
	at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
	at org.junit.platform.engine.support.hierarchical.NodeTestTask.executeRecursively(NodeTestTask.java:143)
	at org.junit.platform.engine.support.hierarchical.NodeTestTask.execute(NodeTestTask.java:100)
	at org.junit.platform.engine.support.hierarchical.ForkJoinPoolHierarchicalTestExecutorService$ExclusiveTask.exec(ForkJoinPoolHierarchicalTestExecutorService.java:274)
	at java.base/java.util.concurrent.ForkJoinTask.doExec(ForkJoinTask.java:387)
	at java.base/java.util.concurrent.ForkJoinPool$WorkQueue.tryRemoveAndExec(ForkJoinPool.java:1351)
	at java.base/java.util.concurrent.ForkJoinTask.awaitDone(ForkJoinTask.java:422)
	at java.base/java.util.concurrent.ForkJoinTask.get(ForkJoinTask.java:991)
	at org.junit.platform.engine.support.hierarchical.NodeTestTask$DefaultDynamicTestExecutor.awaitFinished(NodeTestTask.java:241)
	at org.junit.jupiter.engine.descriptor.TestFactoryTestDescriptor.lambda$invokeTestMethod$1(TestFactoryTestDescriptor.java:115)
	at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
	at org.junit.jupiter.engine.descriptor.TestFactoryTestDescriptor.invokeTestMethod(TestFactoryTestDescriptor.java:95)
	at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.execute(TestMethodTestDescriptor.java:138)
	at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.execute(TestMethodTestDescriptor.java:68)
	at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$6(NodeTestTask.java:156)
	at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
	at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$8(NodeTestTask.java:146)
	at org.junit.platform.engine.support.hierarchical.Node.around(Node.java:137)
	at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$9(NodeTestTask.java:144)
	at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
	at org.junit.platform.engine.support.hierarchical.NodeTestTask.executeRecursively(NodeTestTask.java:143)
	at org.junit.platform.engine.support.hierarchical.NodeTestTask.execute(NodeTestTask.java:100)
	at org.junit.platform.engine.support.hierarchical.ForkJoinPoolHierarchicalTestExecutorService$ExclusiveTask.exec(ForkJoinPoolHierarchicalTestExecutorService.java:274)
	at org.junit.platform.engine.support.hierarchical.ForkJoinPoolHierarchicalTestExecutorService$ExclusiveTask.execSync(ForkJoinPoolHierarchicalTestExecutorService.java:247)
	at org.junit.platform.engine.support.hierarchical.ForkJoinPoolHierarchicalTestExecutorService.invokeAll(ForkJoinPoolHierarchicalTestExecutorService.java:159)
	at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$6(NodeTestTask.java:160)
	at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
	at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$8(NodeTestTask.java:146)
	at org.junit.platform.engine.support.hierarchical.Node.around(Node.java:137)
	at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$9(NodeTestTask.java:144)
	at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
	at org.junit.platform.engine.support.hierarchical.NodeTestTask.executeRecursively(NodeTestTask.java:143)
	at org.junit.platform.engine.support.hierarchical.NodeTestTask.execute(NodeTestTask.java:100)
	at org.junit.platform.engine.support.hierarchical.ForkJoinPoolHierarchicalTestExecutorService$ExclusiveTask.exec(ForkJoinPoolHierarchicalTestExecutorService.java:274)
	at org.junit.platform.engine.support.hierarchical.ForkJoinPoolHierarchicalTestExecutorService$ExclusiveTask.execSync(ForkJoinPoolHierarchicalTestExecutorService.java:247)
	at org.junit.platform.engine.support.hierarchical.ForkJoinPoolHierarchicalTestExecutorService.invokeAll(ForkJoinPoolHierarchicalTestExecutorService.java:159)
	at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$6(NodeTestTask.java:160)
	at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
	at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$8(NodeTestTask.java:146)
	at org.junit.platform.engine.support.hierarchical.Node.around(Node.java:137)
	at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$9(NodeTestTask.java:144)
	at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
	at org.junit.platform.engine.support.hierarchical.NodeTestTask.executeRecursively(NodeTestTask.java:143)
	at org.junit.platform.engine.support.hierarchical.NodeTestTask.execute(NodeTestTask.java:100)
	at org.junit.platform.engine.support.hierarchical.ForkJoinPoolHierarchicalTestExecutorService$ExclusiveTask.exec(ForkJoinPoolHierarchicalTestExecutorService.java:274)
	at java.base/java.util.concurrent.ForkJoinTask.doExec(ForkJoinTask.java:387)
	at java.base/java.util.concurrent.ForkJoinPool$WorkQueue.topLevelExec(ForkJoinPool.java:1312)
	at java.base/java.util.concurrent.ForkJoinPool.scan(ForkJoinPool.java:1843)
	at java.base/java.util.concurrent.ForkJoinPool.runWorker(ForkJoinPool.java:1808)
	at java.base/java.util.concurrent.ForkJoinWorkerThread.run(ForkJoinWorkerThread.java:188)
withSingleplayer {
waitForPlayer()

server.runOnServer { minecraftServer ->
val player = minecraftServer.playerList.players.first()
player.inventory.setItem(0, ItemStack(Items.PAPER))
MenuRegistry.openExtendedMenu(
player,
SimpleMenuProvider(
{ id, inv, p -> TestItemMenu(id, inv, PlayerInventoryItemAccess(p, 0, Items.PAPER)) },
Component.literal("Test Item Menu"),
),
) { buf -> buf.writeVarInt(0) }
}

clientContext.waitForScreen<TestItemContainerScreen>()
clientContext.waitForComposeIdle()
// One extra tick so the client's just-reported SlotData round-trips back through
// the server (ComposeContainerMenuBase.updateSlotData -> serverbound SlotData ->
// menu.applySlotData()) and the server's own slot list is actually built.
clientContext.waitTicks(2)

server.runOnServer { minecraftServer ->
val player = minecraftServer.playerList.players.first()
val menu = player.containerMenu as? TestItemMenu
?: error("Expected the player's current menu to be a TestItemMenu, was ${player.containerMenu}")

val excludedSlot = menu.slots.firstOrNull { it.container === player.inventory && it.containerSlot == 0 }
?: error("Expected a player-inventory slot bound to container index 0 among ${menu.slots.size} slots")
if (excludedSlot.mayPlace(ItemStack(Items.STONE))) fail("Expected the excluded slot to reject placement")
if (excludedSlot.mayPickup(player)) fail("Expected the excluded slot to reject pickup")

val excludedIndex = menu.slots.indexOf(excludedSlot)
val result = menu.quickMoveStack(player, excludedIndex)
if (!result.isEmpty) fail("Expected quickMoveStack on the excluded slot to return an empty stack, got $result")
}
}
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,117 @@
package net.kernelpanicsoft.archie.test.gametest

import earth.terrarium.common_storage_lib.storage.base.UpdateManager
import net.kernelpanicsoft.archie.gui.item.PlayerInventoryItemAccess
import net.kernelpanicsoft.archie.gametest.internal.EMPTY
import net.kernelpanicsoft.archie.test.TestItemMenu
import net.minecraft.gametest.framework.GameTest
import net.minecraft.gametest.framework.GameTestHelper
import net.minecraft.world.item.ItemStack
import net.minecraft.world.item.Items

/**
* GameTest coverage for [net.kernelpanicsoft.archie.gui.item.ComposeItemContainerMenu], via the
* fixture [TestItemMenu] (declared directly against a real
* [net.minecraft.gametest.framework.GameTestHelper.makeMockServerPlayerInLevel] player - no
* network round trip, matching [net.kernelpanicsoft.archie.test.gametest.CapabilityLookupTests]'s
* "construct against real infrastructure directly" style).
*
* Every test that constructs a [TestItemMenu] here must explicitly call `menu.removed(player)`
* once done (unless the test is specifically about the auto-close path) - `onMenuOpened()`
* unconditionally registers the menu with the real, process-wide
* [net.kernelpanicsoft.archie.gui.item.ItemStateManager], whose real tick listener is genuinely
* running for the whole GameTest server process, not just this test method. A menu left
* registered keeps getting ticked (and its `itemAccess.stillValid(player)` re-checked) by every
* *later* test in this run, not just this one - confirmed the hard way: an earlier draft of this
* suite left menus registered, and a later test's `itemAccess.stillValid` flip cascaded into an
* unrelated packet-registration crash days after this test method had already returned.
*
* Not covered here - manual-only, matching the Phase 3 plan's own carve-out for full on-screen
* GUI open/close/shift-click flow (real Compose rendering + real player interaction isn't
* GameTest-covered anywhere else in this codebase either):
* - The excluded player-inventory slot's `mayPlace`/`mayPickup`/`quickMoveStack` behavior - see
* [ComposeItemContainerMenuClientTests] instead, which covers exactly this via a real menu-open
* round trip (this class can't: it needs
* [net.kernelpanicsoft.archie.gui.ComposeContainerMenuBase.updateSlotData] to have run, which
* sends an outbound `SlotData` packet unconditionally, and packet registration itself is
* deliberately skipped for a server-only GameTest run - `Archie.kt`'s own
* `ArchieNetworkChannel.init()` gating, unrelated to this feature).
*/
@Suppress("unused")
class ComposeItemContainerMenuTests
{
@GameTest(template = EMPTY)
fun GameTestHelper.testSlotContentPersistsThroughHolder()
{
val player = makeMockServerPlayerInLevel()
player.inventory.setItem(0, ItemStack(Items.PAPER))
val menu = TestItemMenu(1, player.inventory, PlayerInventoryItemAccess(player, 0, Items.PAPER))

menu.items[0].set(ItemStack(Items.DIAMOND, 5))
// ArchieItemSlot.set() only mutates in-memory state - persistence is triggered externally,
// the same way ArchieItemMenuSlot.setChanged() (a real vanilla Slot's hook) does it.
UpdateManager.batch(menu.items)

// A second, independent menu constructed against the same real backing stack re-loads
// fresh from its persisted NBT (ItemStackNBTHolderImpl.init reloads every construction) -
// this is a genuine round trip through the public API, not just reading `menu`'s own
// still-live in-memory ArchieItemStorage back.
val reloaded = TestItemMenu(11, player.inventory, PlayerInventoryItemAccess(player, 0, Items.PAPER))
val item = reloaded.items[0].getItem()
menu.removed(player)
reloaded.removed(player)
if (item.item != Items.DIAMOND || item.count != 5)
fail("Expected the item written through menu.items to persist onto the real backing stack, got $item")
succeed()
}

@GameTest(template = EMPTY)
fun GameTestHelper.testStillValidReflectsItemAccess()
{
val player = makeMockServerPlayerInLevel()
player.inventory.setItem(0, ItemStack(Items.PAPER))
val menu = TestItemMenu(4, player.inventory, PlayerInventoryItemAccess(player, 0, Items.PAPER))

val initiallyValid = menu.stillValid(player)
player.inventory.setItem(0, ItemStack(Items.STONE))
val validAfterCorruption = menu.stillValid(player)
menu.removed(player)

if (!initiallyValid) fail("Expected stillValid to be true while the backing stack still matches")
if (validAfterCorruption) fail("Expected stillValid to be false once the backing stack no longer matches")
succeed()
}

@GameTest(template = EMPTY)
fun GameTestHelper.testSyncedFieldRegistersSerializer()
{
val player = makeMockServerPlayerInLevel()
player.inventory.setItem(0, ItemStack(Items.PAPER))
val menu = TestItemMenu(5, player.inventory, PlayerInventoryItemAccess(player, 0, Items.PAPER))

val registered = menu.itemState.propertySerializers.containsKey("counter")
menu.removed(player)

if (!registered)
fail("Expected the @Sync-annotated `counter` field to have registered a serializer in itemState at declaration time")
succeed()
}

@GameTest(template = EMPTY)
fun GameTestHelper.testItemStateManagerForceClosesInvalidMenu()
{
val player = makeMockServerPlayerInLevel()
player.inventory.setItem(0, ItemStack(Items.PAPER))
val menu = TestItemMenu(6, player.inventory, PlayerInventoryItemAccess(player, 0, Items.PAPER))
player.containerMenu = menu

// Corrupt the backing stack so itemAccess.stillValid(player) flips false. No explicit
// removed() call here - the whole point of this test is that ItemStateManager's real tick
// loop closes (and so unregisters) it on its own.
player.inventory.setItem(0, ItemStack(Items.STONE))

succeedWhen {
if (player.containerMenu === menu) fail("Expected ItemStateManager's tick loop to force-close the menu via player.closeContainer() once itemAccess reports it invalid")
}
}
}
Original file line number Diff line number Diff line change
@@ -1,12 +1,14 @@
package net.kernelpanicsoft.archie.test

import net.kernelpanicsoft.archie.Archie
import net.kernelpanicsoft.archie.gui.item.PlayerInventoryItemAccess
import net.kernelpanicsoft.archie.registries.ADeferredRegistryHolder
import dev.architectury.registry.menu.MenuRegistry
import net.minecraft.core.registries.Registries
import net.minecraft.network.chat.Component
import net.minecraft.world.entity.player.Inventory
import net.minecraft.world.inventory.MenuType
import net.minecraft.world.item.Items

object GuiRegistry : ADeferredRegistryHolder<MenuType<*>>(Archie.MOD, Registries.MENU)
{
Expand All @@ -20,9 +22,20 @@ object GuiRegistry : ADeferredRegistryHolder<MenuType<*>>(Archie.MOD, Registries
}
}

val TestItemMenu: MenuType<TestItemMenu> by register("test_item_menu") {
MenuRegistry.ofExtended { id, inventory, buf ->
TestItemMenu(
id,
inventory,
PlayerInventoryItemAccess(inventory.player, buf.readVarInt(), Items.PAPER)
)
}
}

override fun initClient()
{
MenuRegistry.registerScreenFactory(TestMenu, ::TestScreen)
MenuRegistry.registerScreenFactory(TestItemMenu, ::TestItemContainerScreen)
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
package net.kernelpanicsoft.archie.test

import net.kernelpanicsoft.archie.gui.ComposeContainerScreen
import net.kernelpanicsoft.archie.gui.PlayerSlots
import net.kernelpanicsoft.archie.gui.Slots
import net.kernelpanicsoft.archie.gui.theme.Theme
import net.minecraft.network.chat.Component
import net.minecraft.world.entity.player.Inventory

/** Minimal fixture screen for [TestItemMenu] - just enough layout to drive a real [net.kernelpanicsoft.archie.gui.ComposeContainerMenuBase.updateSlotData] round trip. */
class TestItemContainerScreen(menu: TestItemMenu, playerInventory: Inventory, title: Component) : ComposeContainerScreen<TestItemMenu>(menu, playerInventory, title)
{
init
{
start {
Theme {
Slots("inventory", 9, 1)
PlayerSlots()
}
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
package net.kernelpanicsoft.archie.test

import net.kernelpanicsoft.archie.gui.item.ComposeItemContainerMenu
import net.kernelpanicsoft.archie.gui.item.ItemContainerAccess
import net.kernelpanicsoft.archie.serialization.Sync
import net.minecraft.world.entity.player.Inventory

/** Fixture item-backed menu for [net.kernelpanicsoft.archie.test.gametest.ComposeItemContainerMenuTests]/[net.kernelpanicsoft.archie.test.gametest.ComposeItemContainerMenuClientTests]. */
class TestItemMenu(id: Int, inventory: Inventory, access: ItemContainerAccess) :
ComposeItemContainerMenu<TestItemMenu>(GuiRegistry.TestItemMenu, id, inventory, access)
{
@Sync
var counter by holder.intField()

val items by holder.itemField(9)

override fun registerSlotHandlers()
{
handler("inventory", items)
}
}
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
package net.kernelpanicsoft.archie.test

import net.kernelpanicsoft.archie.gui.ComposeContainerMenu
import net.kernelpanicsoft.archie.gui.ComposeBlockContainerMenu
import net.minecraft.world.entity.player.Inventory

class TestMenu(id: Int, inventory: Inventory, tile: TestTile) : ComposeContainerMenu<TestTile, TestMenu>(GuiRegistry.TestMenu, id, inventory, tile)
class TestMenu(id: Int, inventory: Inventory, tile: TestTile) : ComposeBlockContainerMenu<TestTile, TestMenu>(GuiRegistry.TestMenu, id, inventory, tile)
{
val rows: Int = tile.items.size() / 9

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ import net.kernelpanicsoft.archie.gui.util.KColor
import net.minecraft.network.chat.Component
import net.minecraft.world.entity.player.Inventory

class TestScreen(menu: TestMenu, playerInventory: Inventory, title: Component) : ComposeContainerScreen<TestMenu, TestTile>(menu, playerInventory,
class TestScreen(menu: TestMenu, playerInventory: Inventory, title: Component) : ComposeContainerScreen<TestMenu>(menu, playerInventory,
title
)
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ import net.neoforged.fml.common.Mod
import net.neoforged.fml.event.lifecycle.FMLClientSetupEvent
import net.neoforged.fml.event.lifecycle.FMLCommonSetupEvent
import net.neoforged.fml.event.lifecycle.FMLConstructModEvent
import net.neoforged.neoforge.client.event.RegisterMenuScreensEvent

@Mod(ArchieTest.MOD_ID)
object ArchieTestNeoForge
Expand All @@ -20,8 +19,10 @@ object ArchieTestNeoForge
MOD_BUS.addListener<FMLCommonSetupEvent> {
ArchieTest.initCommon()
}
MOD_BUS.addListener<RegisterMenuScreensEvent> {
it.register(GuiRegistry.TestMenu, ::TestScreen)
}
// GuiRegistry.initClient() (an ADeferredRegistryHolder override) now registers its own
// screen factories at the correct time on its own - see
// net.kernelpanicsoft.archie.registries.scheduleEarlyClientRegistration. A manual
// RegisterMenuScreensEvent listener duplicating that here would now throw
// "Duplicate attempt to register screen" instead of silently doing nothing.
}
}
8 changes: 8 additions & 0 deletions Archie/common/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -125,8 +125,16 @@ tasks {
dependsOn(verifyGuiSpriteAssets)
}

// Keep stubUnfulfilledExpects()'s generated throwing-actual stubs out of what gets published -
// a consumer with both this jar and a real actual on its classpath must only ever see the
// real one, or Kotlin's actual-resolution can end up preferring the stub.
jar {
from(sourceSets.main.get().output)
exclude("**/*StubKt.class")
}

sourcesJar {
exclude("**/*Stub.kt")
}

test {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import net.kernelpanicsoft.archie.gametest.AGameTestSide
import net.kernelpanicsoft.archie.gametest.ThreadingImpl
import net.kernelpanicsoft.archie.gametest.internal.ArchieGameTest
import net.kernelpanicsoft.archie.gui.blockentity.BlockEntityStateManager
import net.kernelpanicsoft.archie.gui.item.ItemStateManager
import net.kernelpanicsoft.archie.gui.theme.ThemeManifestResourceListener
import net.kernelpanicsoft.archie.gui.theme.ThemeResourceListener
import net.kernelpanicsoft.archie.networking.ArchieNetworkChannel
Expand Down Expand Up @@ -74,6 +75,7 @@ object Archie
ArchieNetworkChannel.init()
}
BlockEntityStateManager.init()
ItemStateManager.init()

ABuiltinIngredients.init()
ABuiltinConditions.init()
Expand Down
Loading
Loading