mirror of
https://github.com/Minestom/Minestom.git
synced 2025-01-28 19:11:45 +01:00
* Calculates the squared distance from the player's eye position to the closest point on the entity's bounding box, this also improves the accuracy of interaction range enforcement (preventing interactions w/ entities outside the allowed range regardless of bounding box size) * unit testing * unit testing
This commit is contained in:
parent
d8c5831f4e
commit
4d3154b68c
@ -1,7 +1,9 @@
|
||||
package net.minestom.server.listener;
|
||||
|
||||
import net.minestom.server.ServerFlag;
|
||||
import net.minestom.server.collision.BoundingBox;
|
||||
import net.minestom.server.coordinate.Point;
|
||||
import net.minestom.server.coordinate.Pos;
|
||||
import net.minestom.server.coordinate.Vec;
|
||||
import net.minestom.server.entity.Entity;
|
||||
import net.minestom.server.entity.LivingEntity;
|
||||
@ -20,8 +22,11 @@ public class UseEntityListener {
|
||||
return;
|
||||
|
||||
if (ServerFlag.ENFORCE_INTERACTION_LIMIT) {
|
||||
double range = Math.pow(player.getAttributeValue(Attribute.ENTITY_INTERACTION_RANGE) + 1, 2); // Add 1 additional block for people with less than stellar ping
|
||||
if (player.getDistanceSquared(entity) > range) {
|
||||
final double maxDistanceSquared = Math.pow(player.getAttributeValue(Attribute.ENTITY_INTERACTION_RANGE) + 1, 2);
|
||||
|
||||
final double distSquared = getDistSquared(player, entity);
|
||||
|
||||
if (distSquared > maxDistanceSquared) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
@ -36,4 +41,33 @@ public class UseEntityListener {
|
||||
EventDispatcher.call(new PlayerEntityInteractEvent(player, entity, interactAt.hand(), interactPosition));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static double getDistSquared(Player player, Entity entity) {
|
||||
final Pos playerPos = player.getPosition();
|
||||
final double eyeHeight = player.getEyeHeight();
|
||||
final double px = playerPos.x();
|
||||
final double py = playerPos.y() + eyeHeight;
|
||||
final double pz = playerPos.z();
|
||||
|
||||
final BoundingBox box = entity.getBoundingBox();
|
||||
final double halfWidth = box.width() / 2;
|
||||
final double height = box.height();
|
||||
final Pos entityPos = entity.getPosition();
|
||||
|
||||
final double minX = entityPos.x() - halfWidth;
|
||||
final double maxX = entityPos.x() + halfWidth;
|
||||
final double minY = entityPos.y();
|
||||
final double maxY = entityPos.y() + height;
|
||||
final double minZ = entityPos.z() - halfWidth;
|
||||
final double maxZ = entityPos.z() + halfWidth;
|
||||
|
||||
final double clampX = Math.max(minX, Math.min(px, maxX));
|
||||
final double clampY = Math.max(minY, Math.min(py, maxY));
|
||||
final double clampZ = Math.max(minZ, Math.min(pz, maxZ));
|
||||
|
||||
final double dx = px - clampX;
|
||||
final double dy = py - clampY;
|
||||
final double dz = pz - clampZ;
|
||||
return dx * dx + dy * dy + dz * dz;
|
||||
}
|
||||
}
|
@ -0,0 +1,106 @@
|
||||
package net.minestom.server.listener;
|
||||
|
||||
import net.minestom.server.coordinate.Pos;
|
||||
import net.minestom.server.entity.Entity;
|
||||
import net.minestom.server.entity.EntityType;
|
||||
import net.minestom.server.entity.Player;
|
||||
import net.minestom.server.entity.PlayerHand;
|
||||
import net.minestom.server.entity.attribute.Attribute;
|
||||
import net.minestom.server.event.player.PlayerEntityInteractEvent;
|
||||
import net.minestom.server.instance.Instance;
|
||||
import net.minestom.server.network.packet.client.play.ClientInteractEntityPacket;
|
||||
import net.minestom.testing.Env;
|
||||
import net.minestom.testing.EnvTest;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.*;
|
||||
|
||||
@EnvTest
|
||||
public class UseEntityListenerTest {
|
||||
|
||||
private Player player;
|
||||
private Entity targetEntity;
|
||||
private boolean eventWasCalled;
|
||||
|
||||
@BeforeEach
|
||||
public void setup(Env env) {
|
||||
Instance instance = env.createFlatInstance();
|
||||
|
||||
player = env.createPlayer(instance, new Pos(0, 0, 0));
|
||||
player.getAttribute(Attribute.ENTITY_INTERACTION_RANGE).setBaseValue(5.0);
|
||||
|
||||
targetEntity = new Entity(EntityType.SLIME);
|
||||
targetEntity.setInstance(instance, new Pos(2, 0, 2)).join();
|
||||
|
||||
eventWasCalled = false;
|
||||
|
||||
player.eventNode().addListener(PlayerEntityInteractEvent.class, event -> {
|
||||
if (event.getPlayer().equals(player) && event.getTarget().equals(targetEntity)) {
|
||||
eventWasCalled = true;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testInteractionWithinRange() {
|
||||
ClientInteractEntityPacket packet = new ClientInteractEntityPacket(
|
||||
targetEntity.getEntityId(),
|
||||
new ClientInteractEntityPacket.InteractAt(0, 0, 0, PlayerHand.MAIN),
|
||||
false
|
||||
);
|
||||
|
||||
UseEntityListener.useEntityListener(packet, player);
|
||||
assertTrue(eventWasCalled, "Expected PlayerEntityInteractEvent to be called for nearby target");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testInteractionOutOfRange() {
|
||||
player.getAttribute(Attribute.ENTITY_INTERACTION_RANGE).setBaseValue(1.0);
|
||||
|
||||
targetEntity.teleport(new Pos(10, 0, 10)).join();
|
||||
ClientInteractEntityPacket packet = new ClientInteractEntityPacket(
|
||||
targetEntity.getEntityId(),
|
||||
new ClientInteractEntityPacket.InteractAt(0, 0, 0, PlayerHand.MAIN),
|
||||
false
|
||||
);
|
||||
|
||||
eventWasCalled = false;
|
||||
UseEntityListener.useEntityListener(packet, player);
|
||||
assertFalse(eventWasCalled, "Expected PlayerEntityInteractEvent NOT to be called for out-of-range target");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testInteractionConsideringHitboxAndEyePosition() {
|
||||
player.getAttribute(Attribute.ENTITY_INTERACTION_RANGE).setBaseValue(1.5);
|
||||
|
||||
targetEntity.teleport(new Pos(1.6, 0, 0)).join();
|
||||
|
||||
ClientInteractEntityPacket packet = new ClientInteractEntityPacket(
|
||||
targetEntity.getEntityId(),
|
||||
new ClientInteractEntityPacket.InteractAt(0, 0, 0, PlayerHand.MAIN),
|
||||
false
|
||||
);
|
||||
|
||||
eventWasCalled = false;
|
||||
UseEntityListener.useEntityListener(packet, player);
|
||||
assertTrue(eventWasCalled, "Expected PlayerEntityInteractEvent to be called considering hitbox size and eye position");
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void testInteractionConsideringEyeHeight() {
|
||||
player.teleport(new Pos(0, 1.6, 0)).join();
|
||||
targetEntity.teleport(new Pos(0, 1.6, 2)).join();
|
||||
|
||||
ClientInteractEntityPacket packet = new ClientInteractEntityPacket(
|
||||
targetEntity.getEntityId(),
|
||||
new ClientInteractEntityPacket.InteractAt(0, 0, 0, PlayerHand.MAIN),
|
||||
false
|
||||
);
|
||||
|
||||
eventWasCalled = false;
|
||||
UseEntityListener.useEntityListener(packet, player);
|
||||
assertTrue(eventWasCalled, "Expected PlayerEntityInteractEvent to be called considering eye height");
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user