Allow floorless leafy link destinations
Build / build (push) Canceled after 0s

This commit is contained in:
dnorberg committed 2026-09-21 09:03:59 -04:00
1 parent 3232051ad4
commit aed8d6a5c3
4 files changed
+20 -11

No files matched your search

+3
View File
@@ -142,6 +142,9 @@ Version 0.1.20 uses a cubic 18-by-18-by-18 snapshot. The renderer remaps the
link point within that cube so its existing target-centered camera framing is link point within that cube so its existing target-centered camera framing is
unchanged. unchanged.
Version 0.1.21 treats air and leaf blocks as passable linking destinations;
links no longer require a solid floor beneath the player.
The preview code is separated behind a renderer interface and uses an isolated The preview code is separated behind a renderer interface and uses an isolated
Ponder level populated from server-authoritative destination snapshots. Ponder level populated from server-authoritative destination snapshots.
+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. # 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 mod_license=LGPL-3.0-or-later
# The mod version. See https://semver.org/ # The mod version. See https://semver.org/
mod_version=0.1.20 mod_version=0.1.21
# The group ID for the mod. It is only important when publishing as an artifact to a Maven repository. # 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. # This should match the base package used for the mod sources.
# See https://maven.apache.org/guides/mini/guide-naming-conventions.html # See https://maven.apache.org/guides/mini/guide-naming-conventions.html
@@ -4,9 +4,9 @@ import java.util.Comparator;
import java.util.Optional; import java.util.Optional;
import java.util.stream.Stream; import java.util.stream.Stream;
import net.minecraft.core.BlockPos; import net.minecraft.core.BlockPos;
import net.minecraft.core.Direction;
import net.minecraft.server.level.ServerLevel; import net.minecraft.server.level.ServerLevel;
import net.minecraft.server.level.ServerPlayer; import net.minecraft.server.level.ServerPlayer;
import net.minecraft.tags.BlockTags;
import net.minecraft.world.level.border.WorldBorder; import net.minecraft.world.level.border.WorldBorder;
import net.minecraft.world.phys.Vec3; import net.minecraft.world.phys.Vec3;
@@ -63,12 +63,13 @@ public final class LinkingService {
private static boolean isSafe(ServerLevel level, BlockPos feet) { private static boolean isSafe(ServerLevel level, BlockPos feet) {
BlockPos head = feet.above(); BlockPos head = feet.above();
BlockPos floor = feet.below(); return isPassableSpawnSpace(level, feet) && isPassableSpawnSpace(level, head);
return level.getBlockState(feet).getCollisionShape(level, feet).isEmpty() }
&& level.getFluidState(feet).isEmpty()
&& level.getBlockState(head).getCollisionShape(level, head).isEmpty() private static boolean isPassableSpawnSpace(ServerLevel level, BlockPos position) {
&& level.getFluidState(head).isEmpty() var state = level.getBlockState(position);
&& level.getBlockState(floor).isFaceSturdy(level, floor, Direction.UP); return level.getFluidState(position).isEmpty()
&& (state.getCollisionShape(level, position).isEmpty() || state.is(BlockTags.LEAVES));
} }
public enum LinkResult { public enum LinkResult {
@@ -13,6 +13,7 @@ import net.minecraft.nbt.CompoundTag;
import net.minecraft.server.level.ServerPlayer; import net.minecraft.server.level.ServerPlayer;
import net.minecraft.world.item.ItemStack; import net.minecraft.world.item.ItemStack;
import net.minecraft.world.level.block.Blocks; import net.minecraft.world.level.block.Blocks;
import net.minecraft.world.phys.Vec3;
import net.neoforged.neoforge.gametest.GameTestHolder; import net.neoforged.neoforge.gametest.GameTestHolder;
import net.neoforged.neoforge.gametest.PrefixGameTestTemplate; import net.neoforged.neoforge.gametest.PrefixGameTestTemplate;
@@ -44,8 +45,10 @@ public final class LinkingGameTests {
} }
@GameTest(template = "empty") @GameTest(template = "empty")
public static void travelUsesRecordedSafePosition(GameTestHelper helper) { public static void travelUsesLeafyDestinationWithoutFloor(GameTestHelper helper) {
helper.setBlock(ORIGIN.below(), Blocks.STONE); helper.setBlock(ORIGIN.below(), Blocks.AIR);
helper.setBlock(ORIGIN, Blocks.OAK_LEAVES);
helper.setBlock(ORIGIN.above(), Blocks.OAK_LEAVES);
BlockPos destination = helper.absolutePos(ORIGIN); BlockPos destination = helper.absolutePos(ORIGIN);
ServerPlayer player = helper.makeMockServerPlayerInLevel(); ServerPlayer player = helper.makeMockServerPlayerInLevel();
LinkTarget target = new LinkTarget(GlobalPos.of(helper.getLevel().dimension(), destination), 90.0F, 15.0F); LinkTarget target = new LinkTarget(GlobalPos.of(helper.getLevel().dimension(), destination), 90.0F, 15.0F);
@@ -53,8 +56,10 @@ public final class LinkingGameTests {
LinkingService.LinkResult result = LinkingService.travel(player, target); LinkingService.LinkResult result = LinkingService.travel(player, target);
helper.assertValueEqual(result, LinkingService.LinkResult.SUCCESS, helper.assertValueEqual(result, LinkingService.LinkResult.SUCCESS,
"A Linking Book must travel to a clear location with a sturdy floor"); "A Linking Book must accept leaf blocks even without a floor");
helper.assertTrue(player.level() == helper.getLevel(), "Travel must remain in the recorded dimension"); helper.assertTrue(player.level() == helper.getLevel(), "Travel must remain in the recorded dimension");
helper.assertTrue(player.position().distanceToSqr(Vec3.atBottomCenterOf(destination)) < 0.001D,
"The recorded leafy destination itself must be selected");
helper.assertTrue(Math.abs(player.getYRot() - 90.0F) < 0.01F && Math.abs(player.getXRot() - 15.0F) < 0.01F, helper.assertTrue(Math.abs(player.getYRot() - 90.0F) < 0.01F && Math.abs(player.getXRot() - 15.0F) < 0.01F,
"Travel must restore the recorded orientation"); "Travel must restore the recorded orientation");
helper.succeed(); helper.succeed();