From 4c7f5d77a46c85c0343451d18ae63bb0fa25d2e9 Mon Sep 17 00:00:00 2001 From: 3TUSK Date: Fri, 4 Aug 2023 13:02:03 -0700 Subject: [PATCH] 0.7.11: Hotfix: vanilla /tp fails when initiator is in wildness and target is in an area --- build.gradle | 2 +- .../impl/AreaEntitySelectorChecker.java | 78 +++++++++++-------- .../impl/AreaEntitySelectorCheckerTest.java | 20 ++++- 3 files changed, 66 insertions(+), 34 deletions(-) diff --git a/build.gradle b/build.gradle index c316597..6dfeaf9 100644 --- a/build.gradle +++ b/build.gradle @@ -21,7 +21,7 @@ apply plugin: 'eclipse' apply plugin: 'maven-publish' apply plugin: 'org.spongepowered.mixin' -version = '0.7.10' +version = '0.7.11' group = 'org.teacon' archivesBaseName = 'AreaControl-Forge-1.20' diff --git a/src/main/java/org/teacon/areacontrol/impl/AreaEntitySelectorChecker.java b/src/main/java/org/teacon/areacontrol/impl/AreaEntitySelectorChecker.java index 1573744..df0bfa5 100644 --- a/src/main/java/org/teacon/areacontrol/impl/AreaEntitySelectorChecker.java +++ b/src/main/java/org/teacon/areacontrol/impl/AreaEntitySelectorChecker.java @@ -4,6 +4,7 @@ import net.minecraft.world.entity.Entity; import net.minecraft.world.level.BaseCommandBlock; import net.minecraft.world.phys.Vec3; +import org.jetbrains.annotations.NotNull; import org.teacon.areacontrol.AreaControlConfig; import org.teacon.areacontrol.AreaManager; import org.teacon.areacontrol.api.AreaProperties; @@ -45,22 +46,36 @@ public static boolean check(CommandSourceStack sourceStack, Entity e) { selectFromChildFallBack = AreaControlConfig.allowCBSelectingFromChild; selectFromParentFallBack = AreaControlConfig.allowCBSelectingFromParent; } - if (selectFromChild != null) { - var area = AreaManager.INSTANCE.findBy(sourceStack.getLevel(), sourceStack.getPosition()); - // Get the area in which the target entity locates. - // Do note that, EntitySelector can select entities from a different dimension, - // so we must use the level from the target entity. - final var targetArea = AreaManager.INSTANCE.findBy(e.level(), new Vec3(e.xo, e.yo, e.zo)); - // If the entity is in the same area as the selector initiator, then it may be selected - if (area == targetArea) { - return true; - } - // Otherwise, we follow this procedure to determine. - var currentlyChecking = area; - // 1. Walk up from the area hierarchy tree, checking if all the parent areas - // allow "selecting entities from child area". - // The walking stops at the area that is common ancestor to both the - // origin area and target area. + // If the command source is neither entity nor command block, we will skip the check. + // Otherwise, we will do the actual check. + return selectFromChild == null || check0(sourceStack, e, selectFromChild, selectFromParent, + selectFromChildFallBack, selectFromParentFallBack); + } + + private static boolean check0(CommandSourceStack sourceStack, Entity e, + String selectFromChild, String selectFromParent, + Supplier<@NotNull Boolean> selectFromChildFallBack, + Supplier<@NotNull Boolean> selectFromParentFallBack) { + var area = AreaManager.INSTANCE.findBy(sourceStack.getLevel(), sourceStack.getPosition()); + // Get the area in which the target entity locates. + // Do note that, EntitySelector can select entities from a different dimension, + // so we must use the level from the target entity. + final var targetArea = AreaManager.INSTANCE.findBy(e.level(), new Vec3(e.xo, e.yo, e.zo)); + // If the entity is in the same area as the selector initiator, then it may be selected + if (area == targetArea) { + return true; + } + // Otherwise, we follow this procedure to determine. + var currentlyChecking = area; + // 1. Walk up from the area hierarchy tree, checking if all the parent areas + // allow "selecting entities from child area". + // The walking stops at the area that is common ancestor to both the + // origin area and target area. + // Notably, if currentlyChecking is null, it means that the command source + // locates in the wildness, then there is no need to "walk up the tree". + // Otherwise, we'd have NPE. This is specifically the case for vanilla + // /tp command. + if (currentlyChecking != null) { do { currentlyChecking = AreaManager.INSTANCE.findBy(currentlyChecking.belongingArea); var result = AreaProperties.getBoolOptional(currentlyChecking, selectFromChild); @@ -68,23 +83,22 @@ public static boolean check(CommandSourceStack sourceStack, Entity e) { return false; } } while (!AreaMath.isEnclosing(currentlyChecking, targetArea)); - // 2. If we are at the target area, then we are done, we can select this entity. - // Else, we have to walk down along the hierarchy tree, to the target area. - if (currentlyChecking != targetArea) { - var reverseChecking = targetArea; - // 3. For each area that we encounter, we check if it allows "selecting entities - // from parent area". - do { - var result = AreaProperties.getBoolOptional(reverseChecking, selectFromParent); - if (!result.orElseGet(selectFromParentFallBack)) { - return false; - } - reverseChecking = AreaManager.INSTANCE.findBy(reverseChecking.belongingArea); - } while (reverseChecking != currentlyChecking); - } } - // If the program hits here, it means that we have successfully conducted all checks, - // or there isn't anything to check (because it was server console or other sources). + // 2. If we are at the target area, then we are done, we can select this entity. + // Else, we have to walk down along the hierarchy tree, to the target area. + if (currentlyChecking != targetArea) { + var reverseChecking = targetArea; + // 3. For each area that we encounter, we check if it allows "selecting entities + // from parent area". + do { + var result = AreaProperties.getBoolOptional(reverseChecking, selectFromParent); + if (!result.orElseGet(selectFromParentFallBack)) { + return false; + } + reverseChecking = AreaManager.INSTANCE.findBy(reverseChecking.belongingArea); + } while (reverseChecking != currentlyChecking); + } + // If the program hits here, it means that we have successfully conducted all checks. // Return true as our result. return true; } diff --git a/src/test/java/org/teacon/areacontrol/test/impl/AreaEntitySelectorCheckerTest.java b/src/test/java/org/teacon/areacontrol/test/impl/AreaEntitySelectorCheckerTest.java index 65ffc4a..9040aa3 100644 --- a/src/test/java/org/teacon/areacontrol/test/impl/AreaEntitySelectorCheckerTest.java +++ b/src/test/java/org/teacon/areacontrol/test/impl/AreaEntitySelectorCheckerTest.java @@ -105,7 +105,7 @@ public static void initAreaManager() { @Test public void testSelectingWithinSameAreaByPlayer() { Mockito.when(this.mockLevel.dimension()).thenReturn(Level.OVERWORLD); - var commandSrc = new CommandSourceStack(this.mockEntity, Vec3.ZERO, Vec2.ZERO, this.mockLevel, 4, "Mockito", Component.literal("Mockito"), this.mockServer, null); + var commandSrc = new CommandSourceStack(this.mockPlayer, Vec3.ZERO, Vec2.ZERO, this.mockLevel, 4, "Mockito", Component.literal("Mockito"), this.mockServer, null); // Position mock entity to [0.5, 0.5, 0.5] of overworld (in area A) Mockito.when(this.mockEntity.level()).thenReturn(this.mockLevel); this.mockEntity.xo = 0.0; @@ -142,6 +142,24 @@ public void testSelectingByNeitherPlayerNorCommandBlock() { Assertions.assertTrue(AreaEntitySelectorChecker.check(commandSrc, this.mockEntity)); } + /** + * Test that a player in the wildness can select an entity within an area, provided that + * related properties are set to true. + * Verify that the checker returns {@code true}, and no exception is thrown. + */ + @Test + public void testSelectingByPlayerFromWildness() { + Mockito.when(this.mockLevel.dimension()).thenReturn(Level.OVERWORLD); + var playerPos = new Vec3(100, 100, 100); + var commandSrc = new CommandSourceStack(this.mockPlayer, playerPos, Vec2.ZERO, this.mockLevel, 4, "Mockito", Component.literal("Mockito"), this.mockServer, null); + // Position mock entity to [0.5, 0.5, 0.5] of overworld (in area A) + Mockito.when(this.mockEntity.level()).thenReturn(this.mockLevel); + this.mockEntity.xo = 0.0; + this.mockEntity.yo = 5.0; + this.mockEntity.zo = 0.0; + Assertions.assertTrue(AreaEntitySelectorChecker.check(commandSrc, this.mockEntity)); + } + @Test public void testSelectingByPlayerFromMultiLayerNestedAreas() { // Create a CommandSourceStack with true source being a mock Player, location is [3.5, 3.5, 3.5] (in area G).