Add linking game tests
Build / build (push) Canceled after 0s

This commit is contained in:
dnorberg committed 2026-09-21 08:32:18 -04:00
1 parent 5a9a13f35c
commit b6f321e6cb
5 files changed
+86 -2

No files matched your search

+1
View File
@@ -34,6 +34,7 @@ bin/
### Minecraft Modding ###
run/
logs/
!**/src/**/run/
**/src/generated/**/.cache/
repo/
+6 -1
View File
@@ -54,7 +54,7 @@ the symbol grammar is introduced.
- [x] Server-provided destination snapshots rendered as rotating Ponder scenes.
- [x] Book Stand floating names synchronized from the stored book stack.
- [x] Open the stored book screen from a Book Stand with server-validated linking.
- [ ] Automated in-game tests for binding, persistence, and travel.
- [x] Automated in-game tests for binding, persistence, and travel.
The temporary shapeless Linking Book recipe makes the linking subsystem easy
to test. It can move into the writing/bookbinder progression when that system
@@ -118,6 +118,11 @@ Version 0.1.14 expands the destination snapshot to 9-by-7-by-17 blocks along
the Z axis, rescales the rotating scene to expose that depth, and colors the
panel background with the destination biome's sky color.
Version 0.1.15 adds three dedicated-server GameTests covering Linking Book
component serialization, successful safe travel with restored orientation, and
rejection of fully blocked destinations. The test suite uses a minimal bundled
structure template and runs with `./gradlew runGameTestServer`.
The preview code is separated behind a renderer interface and uses an isolated
Ponder level. The first iteration supplies the animated loading boundary; the
next iteration will add server-authoritative snapshot transport and populate
+1 -1
View File
@@ -33,7 +33,7 @@ mod_name=Rivencraft
# The license of the mod. Review your options at https://choosealicense.com/. All Rights Reserved is the default.
mod_license=LGPL-3.0-or-later
# The mod version. See https://semver.org/
mod_version=0.1.14
mod_version=0.1.15
# The group ID for the mod. It is only important when publishing as an artifact to a Maven repository.
# This should match the base package used for the mod sources.
# See https://maven.apache.org/guides/mini/guide-naming-conventions.html
@@ -0,0 +1,78 @@
package net.dnorberg.rivencraft.gametest;
import net.dnorberg.rivencraft.Rivencraft;
import net.dnorberg.rivencraft.book.LinkTarget;
import net.dnorberg.rivencraft.book.LinkingService;
import net.dnorberg.rivencraft.registry.ModDataComponents;
import net.dnorberg.rivencraft.registry.ModItems;
import net.minecraft.core.BlockPos;
import net.minecraft.core.GlobalPos;
import net.minecraft.gametest.framework.GameTest;
import net.minecraft.gametest.framework.GameTestHelper;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.server.level.ServerPlayer;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.level.block.Blocks;
import net.neoforged.neoforge.gametest.GameTestHolder;
import net.neoforged.neoforge.gametest.PrefixGameTestTemplate;
/** Server-side regression coverage for persistent linking-book data and travel safety. */
@GameTestHolder(Rivencraft.MOD_ID)
@PrefixGameTestTemplate(false)
public final class LinkingGameTests {
private static final BlockPos ORIGIN = new BlockPos(0, 1, 0);
private LinkingGameTests() {
}
@GameTest(template = "empty")
public static void linkTargetSurvivesItemSerialization(GameTestHelper helper) {
LinkTarget expected = new LinkTarget(
GlobalPos.of(helper.getLevel().dimension(), helper.absolutePos(ORIGIN)),
127.5F,
-31.25F
);
ItemStack book = new ItemStack(ModItems.LINKING_BOOK.get());
book.set(ModDataComponents.LINK_TARGET.get(), expected);
CompoundTag serialized = (CompoundTag) book.saveOptional(helper.getLevel().registryAccess());
ItemStack restored = ItemStack.parseOptional(helper.getLevel().registryAccess(), serialized);
helper.assertValueEqual(restored.get(ModDataComponents.LINK_TARGET.get()), expected,
"A Linking Book must retain its dimension, position, and orientation after serialization");
helper.succeed();
}
@GameTest(template = "empty")
public static void travelUsesRecordedSafePosition(GameTestHelper helper) {
helper.setBlock(ORIGIN.below(), Blocks.STONE);
BlockPos destination = helper.absolutePos(ORIGIN);
ServerPlayer player = helper.makeMockServerPlayerInLevel();
LinkTarget target = new LinkTarget(GlobalPos.of(helper.getLevel().dimension(), destination), 90.0F, 15.0F);
LinkingService.LinkResult result = LinkingService.travel(player, target);
helper.assertValueEqual(result, LinkingService.LinkResult.SUCCESS,
"A Linking Book must travel to a clear location with a sturdy floor");
helper.assertTrue(player.level() == helper.getLevel(), "Travel must remain in the recorded dimension");
helper.assertTrue(Math.abs(player.getYRot() - 90.0F) < 0.01F && Math.abs(player.getXRot() - 15.0F) < 0.01F,
"Travel must restore the recorded orientation");
helper.succeed();
}
@GameTest(template = "empty")
public static void travelRejectsBlockedDestination(GameTestHelper helper) {
BlockPos absoluteOrigin = helper.absolutePos(ORIGIN);
for (BlockPos position : BlockPos.betweenClosed(
absoluteOrigin.offset(-4, -2, -4), absoluteOrigin.offset(4, 2, 4))) {
helper.getLevel().setBlockAndUpdate(position, Blocks.STONE.defaultBlockState());
}
ServerPlayer player = helper.makeMockServerPlayerInLevel();
LinkTarget target = new LinkTarget(GlobalPos.of(helper.getLevel().dimension(), absoluteOrigin), 0.0F, 0.0F);
helper.assertValueEqual(LinkingService.travel(player, target), LinkingService.LinkResult.NO_SAFE_DESTINATION,
"Linking must fail rather than placing a player inside a blocked destination");
helper.succeed();
}
}