Skip to content

Commit

Permalink
0.7.11: Hotfix: vanilla /tp fails when initiator is in wildness and t…
Browse files Browse the repository at this point in the history
…arget is in an area
  • Loading branch information
3TUSK committed Aug 4, 2023
1 parent 3df6d42 commit 4c7f5d7
Show file tree
Hide file tree
Showing 3 changed files with 66 additions and 34 deletions.
2 changes: 1 addition & 1 deletion build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -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'

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -45,46 +46,59 @@ 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);
if (!result.orElseGet(selectFromChildFallBack)) {
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;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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).
Expand Down

0 comments on commit 4c7f5d7

Please sign in to comment.