Compare commits
3 Commits
75e292b1b8
...
0fcd02c80d
Author | SHA1 | Date | |
---|---|---|---|
0fcd02c80d | |||
2d950117d3 | |||
2f476ce8f2 |
@ -0,0 +1,218 @@
|
|||||||
|
package fr.pandacube.lib.paper.geometry;
|
||||||
|
|
||||||
|
import org.bukkit.Location;
|
||||||
|
import org.bukkit.util.Vector;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This vector considers Minecraft X Y Z axis orientation,
|
||||||
|
* but consider standard (not Minecraft) radian values for yaw and pitch.<br/>
|
||||||
|
* The length of this Vector (based on {@link #x}, {@link #y} and {@link #z} values)
|
||||||
|
* Is always 1.
|
||||||
|
*
|
||||||
|
* <pre>Yaw :
|
||||||
|
* North (-z) = -PI/2
|
||||||
|
* East (+x) = 0
|
||||||
|
* South (+z) = PI/2
|
||||||
|
* West (-x) = ±PI
|
||||||
|
*
|
||||||
|
* Pitch :
|
||||||
|
* Up (+y) = PI/2
|
||||||
|
* Down (-y) = -PI/2</pre>
|
||||||
|
*/
|
||||||
|
public class DirectionalVector {
|
||||||
|
/**
|
||||||
|
* The X cartesian coordinate of this {@link DirectionalVector}.
|
||||||
|
* It corresponds to the X (west to east) axis in a Minecraft world.
|
||||||
|
*/
|
||||||
|
public final double x;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The Y cartesian coordinate of this {@link DirectionalVector}.
|
||||||
|
* It corresponds to the Y (bottom to top) axis in a Minecraft world.
|
||||||
|
*/
|
||||||
|
public final double y;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The Z cartesian coordinate of this {@link DirectionalVector}.
|
||||||
|
* It corresponds to the Z (north to south) axis in a Minecraft world.
|
||||||
|
*/
|
||||||
|
public final double z;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The azimuthal angle φ (phi) of this {@link DirectionalVector}, in radian.
|
||||||
|
* It corresponds with Minecraft world as follows :
|
||||||
|
* <pre>Yaw :
|
||||||
|
* North (-z) = -PI/2
|
||||||
|
* East (+x) = 0
|
||||||
|
* South (+z) = PI/2
|
||||||
|
* West (-x) = ±PI</pre>
|
||||||
|
*/
|
||||||
|
public final double yaw;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The polar angle θ (theta) of this {@link DirectionalVector}, in radian.
|
||||||
|
* It corresponds with Minecraft world as follows :
|
||||||
|
* <pre>Pitch :
|
||||||
|
* Down (-y) = -PI/2
|
||||||
|
* Up (+y) = PI/2</pre>
|
||||||
|
*/
|
||||||
|
public final double pitch;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initialize this {@link DirectionalVector} with the yaw and pitch
|
||||||
|
* contained in the provided {@link Location}.
|
||||||
|
* {@link Location#getYaw()} and {@link Location#getPitch()} values are automatically
|
||||||
|
* converted to conform {@link #yaw} and {@link #pitch} specification.
|
||||||
|
*/
|
||||||
|
public DirectionalVector(Location l) {
|
||||||
|
this(
|
||||||
|
Math.toRadians(((l.getYaw() + 90) % 360) > 180 ? ((l.getYaw() + 90) % 360) - 360 : ((l.getYaw() + 90) % 360)),
|
||||||
|
-Math.toRadians(l.getPitch())
|
||||||
|
);
|
||||||
|
/* MC : +90 : %360 : >180 -> -360
|
||||||
|
* South (+z) = 0, 360 : 90-450 : 90 : 90 : PI/2
|
||||||
|
* West (-x) = 90 : 180 : 180 : ±180 : ±PI
|
||||||
|
* North (-z) = 180 : 270 : 270 : -90 : -PI/2
|
||||||
|
* East (+x) = 270 : 360 : 0-360 : 0 : 0
|
||||||
|
*/
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param v the vector representing the direction. If v.getX() and v.getZ() are 0,
|
||||||
|
* the yaw will be 0. This may have inconsistency if the vector is calculated
|
||||||
|
* from a {@link Location}'s yaw and pitch. In this case, prefer using
|
||||||
|
* {@link #DirectionalVector(Location)}. The {@link Vector} is
|
||||||
|
* normalized if necessary (does not modify the provided {@link Vector}).
|
||||||
|
*/
|
||||||
|
public DirectionalVector(Vector v) {
|
||||||
|
this(v.getX(), v.getY(), v.getZ());
|
||||||
|
// this((v = v.clone().normalize()).getX(), v.getY(), v.getZ());
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private DirectionalVector(double x, double y, double z) {
|
||||||
|
double vecSize = Math.sqrt(x * x + y * y + z * z);
|
||||||
|
this.x = x / vecSize;
|
||||||
|
this.y = y / vecSize;
|
||||||
|
this.z = z / vecSize;
|
||||||
|
|
||||||
|
if (x == 0.0 && z == 0.0) {
|
||||||
|
pitch = y > 0.0 ? GeometryUtil.PId2 : -GeometryUtil.PId2;
|
||||||
|
yaw = 0;
|
||||||
|
} else {
|
||||||
|
yaw = Math.atan2(z, x);
|
||||||
|
pitch = Math.atan(y / Math.sqrt(x * x + z * z));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private DirectionalVector(double x, double y, double z, double yaw, double pitch) {
|
||||||
|
this.x = x;
|
||||||
|
this.y = y;
|
||||||
|
this.z = z;
|
||||||
|
this.yaw = yaw;
|
||||||
|
this.pitch = pitch;
|
||||||
|
}
|
||||||
|
|
||||||
|
private DirectionalVector(double yaw, double pitch) {
|
||||||
|
this.yaw = yaw;
|
||||||
|
this.pitch = pitch;
|
||||||
|
|
||||||
|
y = Math.sin(pitch);
|
||||||
|
|
||||||
|
double cosPitch = Math.cos(pitch);
|
||||||
|
x = cosPitch * Math.cos(yaw);
|
||||||
|
z = cosPitch * Math.sin(yaw);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public Vector toVector() {
|
||||||
|
return new Vector(x, y, z);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the yaw and the pitch of the provided {@link Location}
|
||||||
|
* with the values inside the current {@link DirectionalVector}
|
||||||
|
* after conversion of these values
|
||||||
|
*/
|
||||||
|
public void putIntoLocation(Location l) {
|
||||||
|
/* std : -PI/2 : <0 ? +2PI : MC
|
||||||
|
* South (+z) = PI/2 : 0 : 0 : 0, 360
|
||||||
|
* West (-x) = ±PI : -3PI/2 - PI/2 : PI/2 : 90
|
||||||
|
* North (-z) = -PI/2 : -PI : PI : 180
|
||||||
|
* East (+x) = 0 : -PI/2 : 3PI/2 : 270
|
||||||
|
*/
|
||||||
|
l.setYaw((float) Math.toDegrees(yaw < GeometryUtil.PId2 ? yaw + GeometryUtil.PIx2 - GeometryUtil.PId2 : yaw - GeometryUtil.PId2));
|
||||||
|
l.setPitch((float) Math.toDegrees(-pitch));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public DirectionalVector getOpposite() {
|
||||||
|
return new DirectionalVector(
|
||||||
|
-x,
|
||||||
|
-y,
|
||||||
|
-z,
|
||||||
|
(yaw > 0 ? (yaw - GeometryUtil.PI) : (yaw + GeometryUtil.PI)),
|
||||||
|
-pitch
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* If the current direction is the player face direction,
|
||||||
|
* this method return the direction of the back of the head.
|
||||||
|
* This is an alias of {@link #getOpposite()}
|
||||||
|
*/
|
||||||
|
public DirectionalVector getBackDirection() {
|
||||||
|
return getOpposite();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* If the current direction is the player face direction,
|
||||||
|
* this method return the direction of the bottom of the head.
|
||||||
|
*/
|
||||||
|
public DirectionalVector getBottomDirection() {
|
||||||
|
return new DirectionalVector(
|
||||||
|
(pitch > 0 ? yaw : (yaw > 0 ? (yaw - GeometryUtil.PI) : (yaw + GeometryUtil.PI))),
|
||||||
|
(pitch > 0 ? (pitch - GeometryUtil.PId2) : (-GeometryUtil.PId2 - pitch))
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* If the current direction is the player face direction,
|
||||||
|
* this method return the direction of the top of the head.
|
||||||
|
*/
|
||||||
|
public DirectionalVector getTopDirection() {
|
||||||
|
return new DirectionalVector(
|
||||||
|
(pitch < 0 ? yaw : (yaw > 0 ? (yaw - GeometryUtil.PI) : (yaw + GeometryUtil.PI))),
|
||||||
|
(pitch < 0 ? (pitch + GeometryUtil.PId2) : (GeometryUtil.PId2 - pitch))
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* If the current direction is the player face direction,
|
||||||
|
* this method return the direction of the left of the head.
|
||||||
|
*/
|
||||||
|
public DirectionalVector getLeftDirection() {
|
||||||
|
return new DirectionalVector(
|
||||||
|
yaw > -GeometryUtil.PId2 ? (yaw - GeometryUtil.PId2) : (yaw - GeometryUtil.PId2 + GeometryUtil.PIx2),
|
||||||
|
0
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* If the current direction is the player face direction,
|
||||||
|
* this method return the direction of the right of the head.
|
||||||
|
*/
|
||||||
|
public DirectionalVector getRightDirection() {
|
||||||
|
return new DirectionalVector(
|
||||||
|
yaw < GeometryUtil.PId2 ? (yaw + GeometryUtil.PId2) : (yaw + GeometryUtil.PId2 - GeometryUtil.PIx2),
|
||||||
|
0
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,155 @@
|
|||||||
|
package fr.pandacube.lib.paper.geometry;
|
||||||
|
|
||||||
|
import org.bukkit.Location;
|
||||||
|
import org.bukkit.entity.Player;
|
||||||
|
import org.bukkit.util.Vector;
|
||||||
|
|
||||||
|
public class GeometryUtil {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Value equal to <code>{@link Math#PI}</code>.
|
||||||
|
*/
|
||||||
|
public static final double PI = Math.PI;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Value equal to <code>{@link Math#PI} / 2</code>.
|
||||||
|
*/
|
||||||
|
public static final double PId2 = PI/2;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Value equal to <code>{@link Math#PI} * 2</code>.
|
||||||
|
*/
|
||||||
|
public static final double PIx2 = PI*2;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Player geometry
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The visual height of a Minecraft player skin, when he is standing up and not sneaking,
|
||||||
|
* from the ground where the player is standing on, to the above of the first layer of the head skin.
|
||||||
|
* It doesn't correspond to the player hit box height.<br/>
|
||||||
|
* <br/>
|
||||||
|
* The value is provided in Minecraft Wiki.
|
||||||
|
*/
|
||||||
|
public static final double PLAYER_SKIN_HEIGHT = 1.85;
|
||||||
|
/**
|
||||||
|
* Value provided by net.minecraft.world.entity.player.Player#getStandingEyeHeight
|
||||||
|
*/
|
||||||
|
public static final double PLAYER_EYE_HEIGHT = 1.62;
|
||||||
|
/**
|
||||||
|
* The visual height of a Minecraft player skin, when he is standing up and sneaking,
|
||||||
|
* from the ground where the player is standing on, to the above of the first layer of the head skin.
|
||||||
|
* It may not correspond to the player hit box height.<br/>
|
||||||
|
* <br/>
|
||||||
|
* The current value is the height of the player's hit box when sneaking. Even if this
|
||||||
|
* is close to the real value (tested in game), this is not the exact value.
|
||||||
|
*/
|
||||||
|
public static final double PLAYER_SKIN_HEIGHT_SNEAK = 1.50;
|
||||||
|
/**
|
||||||
|
* Value provided by net.minecraft.world.entity.player.Player#getStandingEyeHeight
|
||||||
|
*/
|
||||||
|
public static final double PLAYER_EYE_HEIGHT_SNEAK = 1.27;
|
||||||
|
public static final double PLAYER_SKIN_PIXEL_SIZE = PLAYER_SKIN_HEIGHT / 32;
|
||||||
|
public static final double PLAYER_HEAD_ROTATION_HEIGHT = PLAYER_SKIN_PIXEL_SIZE * 24;
|
||||||
|
public static final double PLAYER_HEAD_ROTATION_HEIGHT_SNEAK = PLAYER_HEAD_ROTATION_HEIGHT - (PLAYER_SKIN_HEIGHT - PLAYER_SKIN_HEIGHT_SNEAK);
|
||||||
|
public static final double PLAYER_HEAD_SIZE = PLAYER_SKIN_PIXEL_SIZE * 8;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the {@link Location}s of the 8 vertex of the player head<br/>
|
||||||
|
* This method only work if the player is standing up
|
||||||
|
* (not dead, not gliding, not sleeping).
|
||||||
|
* @param playerLocation the location of the player, generally provided by {@link Player#getLocation()}
|
||||||
|
* @param isSneaking if the player is sneaking. Generally {@link Player#isSneaking()}
|
||||||
|
* @return an array of 8 {@link Location}s with x, y, and z values filled (yaw and pitch are ignored).
|
||||||
|
* <pre>
|
||||||
|
* return[0] // top front left
|
||||||
|
* return[1] // top front right
|
||||||
|
* return[2] // bottom front left
|
||||||
|
* return[3] // bottom front right
|
||||||
|
* return[4] // top back left
|
||||||
|
* return[5] // top back right
|
||||||
|
* return[6] // bottom back left
|
||||||
|
* return[7] // bottom back right
|
||||||
|
* </pre>
|
||||||
|
*/
|
||||||
|
public static Location[] getPlayerHeadGeometry(Location playerLocation, boolean isSneaking) {
|
||||||
|
Location[] headAnglesPoints = new Location[8];
|
||||||
|
|
||||||
|
Location playerHeadRotationLocation = playerLocation.clone()
|
||||||
|
.add(0, isSneaking ? PLAYER_HEAD_ROTATION_HEIGHT_SNEAK : PLAYER_HEAD_ROTATION_HEIGHT, 0);
|
||||||
|
|
||||||
|
DirectionalVector frontDirection = new DirectionalVector(playerHeadRotationLocation);
|
||||||
|
Vector frontHalfVector = frontDirection.toVector().multiply(PLAYER_HEAD_SIZE/2);
|
||||||
|
Vector backHalfDirection = frontDirection.getBackDirection().toVector().multiply(PLAYER_HEAD_SIZE/2);
|
||||||
|
Vector leftHalfVector = frontDirection.getLeftDirection().toVector().multiply(PLAYER_HEAD_SIZE/2);
|
||||||
|
Vector rightHalfVector = frontDirection.getRightDirection().toVector().multiply(PLAYER_HEAD_SIZE/2);
|
||||||
|
Vector topVector = frontDirection.getTopDirection().toVector().multiply(PLAYER_HEAD_SIZE);
|
||||||
|
|
||||||
|
Location bottomFrontMiddle = playerHeadRotationLocation.clone().add(frontHalfVector);
|
||||||
|
Location bottomBackMiddle = playerHeadRotationLocation.clone().add(backHalfDirection);
|
||||||
|
|
||||||
|
Location topFrontMiddle = bottomFrontMiddle.clone().add(topVector);
|
||||||
|
Location topBackMiddle = bottomBackMiddle.clone().add(topVector);
|
||||||
|
|
||||||
|
headAnglesPoints[0] = topFrontMiddle.clone().add(leftHalfVector);
|
||||||
|
headAnglesPoints[1] = topFrontMiddle.clone().add(rightHalfVector);
|
||||||
|
headAnglesPoints[2] = bottomFrontMiddle.clone().add(leftHalfVector);
|
||||||
|
headAnglesPoints[3] = bottomFrontMiddle.clone().add(rightHalfVector);
|
||||||
|
headAnglesPoints[4] = topBackMiddle.clone().add(leftHalfVector);
|
||||||
|
headAnglesPoints[5] = topBackMiddle.clone().add(rightHalfVector);
|
||||||
|
headAnglesPoints[6] = bottomBackMiddle.clone().add(leftHalfVector);
|
||||||
|
headAnglesPoints[7] = bottomBackMiddle.clone().add(rightHalfVector);
|
||||||
|
|
||||||
|
return headAnglesPoints;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if the path from <i>start</i> location to <i>end</i> pass through
|
||||||
|
* the axis aligned bounding box defined by <i>min</i> and <i>max</i>.
|
||||||
|
*/
|
||||||
|
public static boolean hasIntersection(Vector start, Vector end, Vector min, Vector max) {
|
||||||
|
final double epsilon = 0.0001f;
|
||||||
|
|
||||||
|
Vector d = end.clone().subtract(start).multiply(0.5);
|
||||||
|
Vector e = max.clone().subtract(min).multiply(0.5);
|
||||||
|
Vector c = start.clone().add(d).subtract(min.clone().add(max).multiply(0.5));
|
||||||
|
Vector ad = d.clone();
|
||||||
|
ad.setX(Math.abs(ad.getX()));
|
||||||
|
ad.setY(Math.abs(ad.getY()));
|
||||||
|
ad.setZ(Math.abs(ad.getZ()));
|
||||||
|
|
||||||
|
return !(
|
||||||
|
Math.abs(c.getX()) > e.getX() + ad.getX()
|
||||||
|
|| Math.abs(c.getY()) > e.getY() + ad.getY()
|
||||||
|
|| Math.abs(c.getZ()) > e.getX() + ad.getZ()
|
||||||
|
|| Math.abs(d.getY() * c.getZ() - d.getZ() * c.getY()) > e.getY() * ad.getZ() + e.getZ() * ad.getY() + epsilon
|
||||||
|
|| Math.abs(d.getZ() * c.getX() - d.getX() * c.getZ()) > e.getZ() * ad.getX() + e.getX() * ad.getZ() + epsilon
|
||||||
|
|| Math.abs(d.getX() * c.getY() - d.getY() * c.getX()) > e.getX() * ad.getY() + e.getY() * ad.getX() + epsilon
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
@ -1,29 +1,26 @@
|
|||||||
package fr.pandacube.lib.paper.util;
|
package fr.pandacube.lib.paper.geometry.blocks;
|
||||||
|
|
||||||
import java.util.Iterator;
|
|
||||||
|
|
||||||
|
import fr.pandacube.lib.util.RandomUtil;
|
||||||
import org.bukkit.Location;
|
import org.bukkit.Location;
|
||||||
import org.bukkit.World;
|
import org.bukkit.World;
|
||||||
import org.bukkit.block.Block;
|
import org.bukkit.block.Block;
|
||||||
import org.bukkit.entity.Entity;
|
|
||||||
import org.bukkit.util.BlockVector;
|
import org.bukkit.util.BlockVector;
|
||||||
import org.bukkit.util.BoundingBox;
|
import org.bukkit.util.BoundingBox;
|
||||||
import org.bukkit.util.Vector;
|
import org.bukkit.util.Vector;
|
||||||
|
|
||||||
import fr.pandacube.lib.util.RandomUtil;
|
import java.util.Iterator;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Checkpoint represented as a 3D Axis and Block Aligned Bounding Box (sort of AABB).
|
* Block Aligned Bounding Box (sort of AABB).
|
||||||
* Represent the littlest cuboid selection of blocks that contains the bounding box
|
* Represent the littlest cuboid selection of blocks that contains the bounding box
|
||||||
* passed to the constructor.
|
* passed to the constructor.
|
||||||
*/
|
*/
|
||||||
public class AABBBlock implements Iterable<BlockVector>, Cloneable {
|
public class AABBBlock implements BlockSet, Cloneable {
|
||||||
|
|
||||||
public final Vector pos1, pos2;
|
/* package */ final Vector pos1, pos2;
|
||||||
|
|
||||||
private final Vector center;
|
private final Vector center;
|
||||||
|
|
||||||
private final long volume;
|
private final long volume;
|
||||||
|
private BoundingBox bukkitBoundingBox;
|
||||||
|
|
||||||
private AABBBlock(AABBBlock original, int shiftX, int shiftY, int shiftZ) {
|
private AABBBlock(AABBBlock original, int shiftX, int shiftY, int shiftZ) {
|
||||||
Vector shiftVec = new Vector(shiftX, shiftY, shiftZ);
|
Vector shiftVec = new Vector(shiftX, shiftY, shiftZ);
|
||||||
@ -32,10 +29,18 @@ public class AABBBlock implements Iterable<BlockVector>, Cloneable {
|
|||||||
center = original.center.clone().add(shiftVec);
|
center = original.center.clone().add(shiftVec);
|
||||||
volume = original.volume;
|
volume = original.volume;
|
||||||
}
|
}
|
||||||
|
|
||||||
public AABBBlock(Vector p1, Vector p2) {
|
public AABBBlock(Vector p1, Vector p2) {
|
||||||
this(p1.getBlockX(), p1.getBlockY(), p1.getBlockZ(), p2.getBlockX(), p2.getBlockY(), p2.getBlockZ());
|
this(p1.getBlockX(), p1.getBlockY(), p1.getBlockZ(), p2.getBlockX(), p2.getBlockY(), p2.getBlockZ());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public AABBBlock(BoundingBox bb) {
|
||||||
|
pos1 = bb.getMin();
|
||||||
|
pos2 = bb.getMax();
|
||||||
|
center = bb.getCenter();
|
||||||
|
volume = (int) bb.getVolume();
|
||||||
|
bukkitBoundingBox = bb;
|
||||||
|
}
|
||||||
|
|
||||||
public AABBBlock(Location l1, Location l2) {
|
public AABBBlock(Location l1, Location l2) {
|
||||||
this(l1.getBlockX(), l1.getBlockY(), l1.getBlockZ(), l2.getBlockX(), l2.getBlockY(), l2.getBlockZ());
|
this(l1.getBlockX(), l1.getBlockY(), l1.getBlockZ(), l2.getBlockX(), l2.getBlockY(), l2.getBlockZ());
|
||||||
@ -64,6 +69,11 @@ public class AABBBlock implements Iterable<BlockVector>, Cloneable {
|
|||||||
volume = (long) Math.abs(p2x_ - p1x_) * Math.abs(p2x_ - p1x_) * Math.abs(p2x_ - p1x_);
|
volume = (long) Math.abs(p2x_ - p1x_) * Math.abs(p2x_ - p1x_) * Math.abs(p2x_ - p1x_);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public AABBBlock getEnglobingAABB() {
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
public AABBBlock shift(int x, int y, int z) {
|
public AABBBlock shift(int x, int y, int z) {
|
||||||
return new AABBBlock(this, x, y, z);
|
return new AABBBlock(this, x, y, z);
|
||||||
}
|
}
|
||||||
@ -74,22 +84,14 @@ public class AABBBlock implements Iterable<BlockVector>, Cloneable {
|
|||||||
return new AABBBlock(this, 0, 0, 0);
|
return new AABBBlock(this, 0, 0, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean overlaps(Entity e) {
|
|
||||||
return overlaps(e.getBoundingBox());
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean overlaps(BoundingBox bb) {
|
public boolean overlaps(BoundingBox bb) {
|
||||||
return asBukkitBoundingBox().overlaps(bb);
|
return asBukkitBoundingBox().overlaps(bb);
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean isInside(Vector v) {
|
public boolean isInside(Vector v) {
|
||||||
return v.isInAABB(pos1, pos2);
|
return asBukkitBoundingBox().contains(v);
|
||||||
}
|
|
||||||
public boolean isInside(Location l) {
|
|
||||||
return isInside(l.toVector());
|
|
||||||
}
|
|
||||||
public boolean isInside(Entity p) {
|
|
||||||
return isInside(p.getLocation());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public Vector getCenter() {
|
public Vector getCenter() {
|
||||||
@ -101,8 +103,11 @@ public class AABBBlock implements Iterable<BlockVector>, Cloneable {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public BoundingBox asBukkitBoundingBox() {
|
public BoundingBox asBukkitBoundingBox() {
|
||||||
return new BoundingBox(pos1.getX(), pos1.getY(), pos1.getZ(),
|
if (bukkitBoundingBox == null) {
|
||||||
pos2.getX(), pos2.getY(), pos2.getZ());
|
bukkitBoundingBox = new BoundingBox(pos1.getX(), pos1.getY(), pos1.getZ(),
|
||||||
|
pos2.getX(), pos2.getY(), pos2.getZ());
|
||||||
|
}
|
||||||
|
return bukkitBoundingBox;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Vector getRandomPosition() {
|
public Vector getRandomPosition() {
|
||||||
@ -157,6 +162,22 @@ public class AABBBlock implements Iterable<BlockVector>, Cloneable {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
static boolean overlap(AABBBlock aabb1, AABBBlock aabb2) {
|
||||||
|
return aabb1.asBukkitBoundingBox().overlaps(aabb2.asBukkitBoundingBox());
|
||||||
|
}
|
||||||
|
|
||||||
|
static boolean overlap(AABBBlock aabb1, BlockSet bs) {
|
||||||
|
if (!overlap(aabb1, bs.getEnglobingAABB()))
|
||||||
|
return false;
|
||||||
|
AABBBlock intersection = new AABBBlock(aabb1.asBukkitBoundingBox().intersection(bs.getEnglobingAABB().asBukkitBoundingBox()));
|
||||||
|
for (BlockVector bv : intersection)
|
||||||
|
if (bs.isInside(bv))
|
||||||
|
return true;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
@ -0,0 +1,138 @@
|
|||||||
|
package fr.pandacube.lib.paper.geometry.blocks;
|
||||||
|
|
||||||
|
import fr.pandacube.lib.util.IteratorIterator;
|
||||||
|
import fr.pandacube.lib.util.RandomUtil;
|
||||||
|
import org.bukkit.util.BlockVector;
|
||||||
|
import org.bukkit.util.BoundingBox;
|
||||||
|
import org.bukkit.util.Vector;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.Iterator;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public class AABBBlockGroup implements BlockSet {
|
||||||
|
|
||||||
|
public final List<AABBBlock> subAABB;
|
||||||
|
|
||||||
|
private final AABBBlock englobingAABB;
|
||||||
|
|
||||||
|
public AABBBlockGroup(Collection<AABBBlock> in) {
|
||||||
|
if (in.isEmpty())
|
||||||
|
throw new IllegalArgumentException("Provided collection must not be empty.");
|
||||||
|
subAABB = List.copyOf(in);
|
||||||
|
englobingAABB = initEnglobingAABB();
|
||||||
|
}
|
||||||
|
|
||||||
|
public AABBBlockGroup(AABBBlock... in) {
|
||||||
|
this(Arrays.asList(in));
|
||||||
|
}
|
||||||
|
|
||||||
|
private AABBBlock initEnglobingAABB() {
|
||||||
|
Vector pos1 = subAABB.get(0).pos1.clone();
|
||||||
|
Vector pos2 = subAABB.get(0).pos2.clone().add(new Vector(-1, -1, -1));
|
||||||
|
for (int i = 1; i < subAABB.size(); i++) {
|
||||||
|
AABBBlock aabb = subAABB.get(i);
|
||||||
|
pos1.setX(Math.min(pos1.getBlockX(), aabb.pos1.getBlockX()));
|
||||||
|
pos1.setY(Math.min(pos1.getBlockY(), aabb.pos1.getBlockZ()));
|
||||||
|
pos1.setZ(Math.min(pos1.getBlockY(), aabb.pos1.getBlockZ()));
|
||||||
|
pos2.setX(Math.max(pos2.getBlockX(), aabb.pos2.getBlockX() - 1));
|
||||||
|
pos2.setY(Math.max(pos2.getBlockY(), aabb.pos2.getBlockZ() - 1));
|
||||||
|
pos2.setZ(Math.max(pos2.getBlockY(), aabb.pos2.getBlockZ() - 1));
|
||||||
|
}
|
||||||
|
return new AABBBlock(pos1, pos2);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public AABBBlock getEnglobingAABB() {
|
||||||
|
return englobingAABB;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isInside(Vector v) {
|
||||||
|
if (!englobingAABB.isInside(v))
|
||||||
|
return false;
|
||||||
|
for (AABBBlock b : subAABB)
|
||||||
|
if (b.isInside(v))
|
||||||
|
return true;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Vector getRandomPosition() {
|
||||||
|
double[] freq = subAABB.stream().mapToDouble(AABBBlock::getVolume).toArray();
|
||||||
|
int i = RandomUtil.randomIndexOfFrequencies(freq);
|
||||||
|
return subAABB.get(i).getRandomPosition();
|
||||||
|
}
|
||||||
|
|
||||||
|
public long getVolume() {
|
||||||
|
long v = 0;
|
||||||
|
for (AABBBlock b : subAABB)
|
||||||
|
v += b.getVolume();
|
||||||
|
return v;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean overlaps(BoundingBox bb) {
|
||||||
|
if (!englobingAABB.overlaps(bb))
|
||||||
|
return false;
|
||||||
|
for (AABBBlock b : subAABB)
|
||||||
|
if (b.overlaps(bb))
|
||||||
|
return true;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Iterator<BlockVector> iterator() {
|
||||||
|
return IteratorIterator.ofCollectionOfIterator(subAABB.stream().map(AABBBlock::iterator).toList());
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/* package */ static boolean overlap(AABBBlockGroup aabbGroup, AABBBlock aabb) {
|
||||||
|
if (!aabbGroup.englobingAABB.overlaps(aabb))
|
||||||
|
return false;
|
||||||
|
for (AABBBlock b : aabbGroup.subAABB)
|
||||||
|
if (b.overlaps(aabb))
|
||||||
|
return true;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* package */ static boolean overlap(AABBBlockGroup aabbGroup1, AABBBlockGroup aabbGroup2) {
|
||||||
|
if (!aabbGroup1.englobingAABB.overlaps(aabbGroup2.englobingAABB))
|
||||||
|
return false;
|
||||||
|
List<AABBBlock> group1SubList = new ArrayList<>();
|
||||||
|
for (AABBBlock b : aabbGroup1.subAABB) {
|
||||||
|
if (b.overlaps(aabbGroup2.englobingAABB))
|
||||||
|
group1SubList.add(b);
|
||||||
|
}
|
||||||
|
if (group1SubList.isEmpty())
|
||||||
|
return false;
|
||||||
|
List<AABBBlock> group2SubList = new ArrayList<>();
|
||||||
|
for (AABBBlock b : aabbGroup2.subAABB) {
|
||||||
|
if (b.overlaps(aabbGroup1.englobingAABB))
|
||||||
|
group2SubList.add(b);
|
||||||
|
}
|
||||||
|
if (group2SubList.isEmpty())
|
||||||
|
return false;
|
||||||
|
for (AABBBlock b1 : group1SubList)
|
||||||
|
for (AABBBlock b2 : group2SubList)
|
||||||
|
if (b1.overlaps(b2))
|
||||||
|
return true;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
static boolean overlap(AABBBlockGroup aabbGroup, BlockSet bs) {
|
||||||
|
if (!aabbGroup.englobingAABB.overlaps(bs.getEnglobingAABB()))
|
||||||
|
return false;
|
||||||
|
for (AABBBlock b : aabbGroup.subAABB) {
|
||||||
|
if (b.overlaps(bs)) // already checks for englobingAABB before checking block per block
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,64 @@
|
|||||||
|
package fr.pandacube.lib.paper.geometry.blocks;
|
||||||
|
|
||||||
|
import org.bukkit.Location;
|
||||||
|
import org.bukkit.block.Block;
|
||||||
|
import org.bukkit.entity.Entity;
|
||||||
|
import org.bukkit.util.BlockVector;
|
||||||
|
import org.bukkit.util.BoundingBox;
|
||||||
|
import org.bukkit.util.Vector;
|
||||||
|
|
||||||
|
public interface BlockSet extends Iterable<BlockVector> {
|
||||||
|
|
||||||
|
Vector getRandomPosition();
|
||||||
|
long getVolume();
|
||||||
|
AABBBlock getEnglobingAABB();
|
||||||
|
|
||||||
|
|
||||||
|
boolean overlaps(BoundingBox bb);
|
||||||
|
default boolean overlaps(Entity e) {
|
||||||
|
return overlaps(e.getBoundingBox());
|
||||||
|
}
|
||||||
|
default boolean overlaps(BlockSet bs) {
|
||||||
|
if (this instanceof AABBBlock b1) {
|
||||||
|
if (bs instanceof AABBBlock b2)
|
||||||
|
return AABBBlock.overlap(b1, b2);
|
||||||
|
if (bs instanceof AABBBlockGroup bg2)
|
||||||
|
return AABBBlockGroup.overlap(bg2, b1);
|
||||||
|
return AABBBlock.overlap(b1, bs);
|
||||||
|
}
|
||||||
|
if (this instanceof AABBBlockGroup bg1) {
|
||||||
|
if (bs instanceof AABBBlock b2)
|
||||||
|
return AABBBlockGroup.overlap(bg1, b2);
|
||||||
|
if (bs instanceof AABBBlockGroup bg2)
|
||||||
|
return AABBBlockGroup.overlap(bg1, bg2);
|
||||||
|
return AABBBlockGroup.overlap(bg1, bs);
|
||||||
|
}
|
||||||
|
return overlap(this, bs);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
boolean isInside(Vector v);
|
||||||
|
default boolean isInside(Location l) {
|
||||||
|
return isInside(l.toVector());
|
||||||
|
}
|
||||||
|
default boolean isInside(Block b) {
|
||||||
|
return isInside(b.getLocation().add(.5, .5, .5));
|
||||||
|
}
|
||||||
|
default boolean isInside(Entity p) {
|
||||||
|
return isInside(p.getLocation());
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
static boolean overlap(BlockSet bs1, BlockSet bs2) {
|
||||||
|
if (!bs1.getEnglobingAABB().overlaps(bs2.getEnglobingAABB()))
|
||||||
|
return false;
|
||||||
|
AABBBlock intersection = new AABBBlock(bs1.getEnglobingAABB().asBukkitBoundingBox().intersection(bs2.getEnglobingAABB().asBukkitBoundingBox()));
|
||||||
|
for (BlockVector bv : intersection)
|
||||||
|
if (bs1.isInside(bv) && bs2.isInside(bv))
|
||||||
|
return true;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -7,6 +7,7 @@ import fr.pandacube.lib.paper.players.PlayerNonPersistentConfig.Expiration;
|
|||||||
import fr.pandacube.lib.paper.reflect.wrapper.craftbukkit.CraftPlayer;
|
import fr.pandacube.lib.paper.reflect.wrapper.craftbukkit.CraftPlayer;
|
||||||
import fr.pandacube.lib.players.standalone.AbstractOnlinePlayer;
|
import fr.pandacube.lib.players.standalone.AbstractOnlinePlayer;
|
||||||
import fr.pandacube.lib.reflect.wrapper.ReflectWrapper;
|
import fr.pandacube.lib.reflect.wrapper.ReflectWrapper;
|
||||||
|
import io.papermc.paper.entity.TeleportFlag.Relative;
|
||||||
import net.kyori.adventure.text.Component;
|
import net.kyori.adventure.text.Component;
|
||||||
import net.kyori.adventure.title.Title;
|
import net.kyori.adventure.title.Title;
|
||||||
import net.kyori.adventure.title.Title.Times;
|
import net.kyori.adventure.title.Title.Times;
|
||||||
@ -294,6 +295,35 @@ public interface PaperOnlinePlayer extends PaperOffPlayer, AbstractOnlinePlayer
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Relative teleport
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Teleports this player to the specified relative location, using the {@link Relative} flags.
|
||||||
|
* @param relX the relative x coordinate.
|
||||||
|
* @param relY the relative y coordinate.
|
||||||
|
* @param relZ the relative z coordinate.
|
||||||
|
*/
|
||||||
|
@SuppressWarnings("UnstableApiUsage")
|
||||||
|
default void teleportRelatively(float relX, float relY, float relZ) {
|
||||||
|
getBukkitPlayer().teleport(getBukkitPlayer().getLocation().add(relX, relY, relZ), Relative.X, Relative.Y, Relative.Z, Relative.YAW, Relative.PITCH);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Teleports this player to the specified location, using the {@link Relative} flags.
|
||||||
|
* @param destination the destination.
|
||||||
|
*/
|
||||||
|
@SuppressWarnings("UnstableApiUsage")
|
||||||
|
default void teleportRelatively(Location destination) {
|
||||||
|
getBukkitPlayer().teleport(destination, Relative.X, Relative.Y, Relative.Z, Relative.YAW, Relative.PITCH);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Player config
|
* Player config
|
||||||
|
@ -1,59 +0,0 @@
|
|||||||
package fr.pandacube.lib.paper.util;
|
|
||||||
|
|
||||||
import java.util.Collection;
|
|
||||||
import java.util.Iterator;
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
import org.bukkit.Location;
|
|
||||||
import org.bukkit.entity.Entity;
|
|
||||||
import org.bukkit.util.BlockVector;
|
|
||||||
import org.bukkit.util.Vector;
|
|
||||||
|
|
||||||
import fr.pandacube.lib.util.IteratorIterator;
|
|
||||||
import fr.pandacube.lib.util.RandomUtil;
|
|
||||||
|
|
||||||
public class AABBBlockGroup implements Iterable<BlockVector> {
|
|
||||||
|
|
||||||
public final List<AABBBlock> aabbBlocks;
|
|
||||||
|
|
||||||
public AABBBlockGroup(Collection<AABBBlock> in) {
|
|
||||||
aabbBlocks = List.copyOf(in);
|
|
||||||
}
|
|
||||||
|
|
||||||
public AABBBlockGroup(AABBBlock... in) {
|
|
||||||
aabbBlocks = List.of(in);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
public boolean isInside(Vector v) {
|
|
||||||
for (AABBBlock b : aabbBlocks)
|
|
||||||
if (b.isInside(v))
|
|
||||||
return true;
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
public boolean isInside(Location l) {
|
|
||||||
return isInside(l.toVector());
|
|
||||||
}
|
|
||||||
public boolean isInside(Entity p) {
|
|
||||||
return isInside(p.getLocation());
|
|
||||||
}
|
|
||||||
|
|
||||||
public Vector getRandomPosition() {
|
|
||||||
double[] freq = aabbBlocks.stream().mapToDouble(AABBBlock::getVolume).toArray();
|
|
||||||
int i = RandomUtil.randomIndexOfFrequencies(freq);
|
|
||||||
return aabbBlocks.get(i).getRandomPosition();
|
|
||||||
}
|
|
||||||
|
|
||||||
public long getVolume() {
|
|
||||||
long v = 0;
|
|
||||||
for (AABBBlock b : aabbBlocks)
|
|
||||||
v += b.getVolume();
|
|
||||||
return v;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Iterator<BlockVector> iterator() {
|
|
||||||
return IteratorIterator.ofCollectionOfIterator(aabbBlocks.stream().map(AABBBlock::iterator).toList());
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
@ -1,395 +0,0 @@
|
|||||||
package fr.pandacube.lib.paper.util;
|
|
||||||
|
|
||||||
import org.bukkit.Location;
|
|
||||||
import org.bukkit.entity.Player;
|
|
||||||
import org.bukkit.util.Vector;
|
|
||||||
|
|
||||||
public class GeometryUtil {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Value equal to <code>{@link Math#PI}</code>.
|
|
||||||
*/
|
|
||||||
public static final double PI = Math.PI;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Value equal to <code>{@link Math#PI} / 2</code>.
|
|
||||||
*/
|
|
||||||
public static final double PId2 = PI/2;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Value equal to <code>{@link Math#PI} * 2</code>.
|
|
||||||
*/
|
|
||||||
public static final double PIx2 = PI*2;
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Player geometry
|
|
||||||
*/
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The visual height of a Minecraft player skin, when he is standing up and not sneaking,
|
|
||||||
* from the ground where the player is standing on, to the above of the first layer of the head skin.
|
|
||||||
* It doesn't correspond to the player hit box height.<br/>
|
|
||||||
* <br/>
|
|
||||||
* The value is provided in Minecraft Wiki.
|
|
||||||
*/
|
|
||||||
public static final double PLAYER_SKIN_HEIGHT = 1.85;
|
|
||||||
/**
|
|
||||||
* Value provided by net.minecraft.world.entity.player.Player#getStandingEyeHeight
|
|
||||||
*/
|
|
||||||
public static final double PLAYER_EYE_HEIGHT = 1.62;
|
|
||||||
/**
|
|
||||||
* The visual height of a Minecraft player skin, when he is standing up and sneaking,
|
|
||||||
* from the ground where the player is standing on, to the above of the first layer of the head skin.
|
|
||||||
* It may not correspond to the player hit box height.<br/>
|
|
||||||
* <br/>
|
|
||||||
* The current value is the height of the player's hit box when sneaking. Even if this
|
|
||||||
* is close to the real value (tested in game), this is not the exact value.
|
|
||||||
*/
|
|
||||||
public static final double PLAYER_SKIN_HEIGHT_SNEAK = 1.50;
|
|
||||||
/**
|
|
||||||
* Value provided by net.minecraft.world.entity.player.Player#getStandingEyeHeight
|
|
||||||
*/
|
|
||||||
public static final double PLAYER_EYE_HEIGHT_SNEAK = 1.27;
|
|
||||||
public static final double PLAYER_SKIN_PIXEL_SIZE = PLAYER_SKIN_HEIGHT / 32;
|
|
||||||
public static final double PLAYER_HEAD_ROTATION_HEIGHT = PLAYER_SKIN_PIXEL_SIZE * 24;
|
|
||||||
public static final double PLAYER_HEAD_ROTATION_HEIGHT_SNEAK = PLAYER_HEAD_ROTATION_HEIGHT - (PLAYER_SKIN_HEIGHT - PLAYER_SKIN_HEIGHT_SNEAK);
|
|
||||||
public static final double PLAYER_HEAD_SIZE = PLAYER_SKIN_PIXEL_SIZE * 8;
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get the {@link Location}s of the 8 vertex of the player head<br/>
|
|
||||||
* This method only work if the player is standing up
|
|
||||||
* (not dead, not gliding, not sleeping).
|
|
||||||
* @param playerLocation the location of the player, generally provided by {@link Player#getLocation()}
|
|
||||||
* @param isSneaking if the player is sneaking. Generally {@link Player#isSneaking()}
|
|
||||||
* @return an array of 8 {@link Location}s with x, y, and z values filled (yaw and pitch are ignored).
|
|
||||||
* <pre>
|
|
||||||
* return[0] // top front left
|
|
||||||
* return[1] // top front right
|
|
||||||
* return[2] // bottom front left
|
|
||||||
* return[3] // bottom front right
|
|
||||||
* return[4] // top back left
|
|
||||||
* return[5] // top back right
|
|
||||||
* return[6] // bottom back left
|
|
||||||
* return[7] // bottom back right
|
|
||||||
* </pre>
|
|
||||||
*/
|
|
||||||
public static Location[] getPlayerHeadGeometry(Location playerLocation, boolean isSneaking) {
|
|
||||||
Location[] headAnglesPoints = new Location[8];
|
|
||||||
|
|
||||||
Location playerHeadRotationLocation = playerLocation.clone()
|
|
||||||
.add(0, isSneaking ? PLAYER_HEAD_ROTATION_HEIGHT_SNEAK : PLAYER_HEAD_ROTATION_HEIGHT, 0);
|
|
||||||
|
|
||||||
DirectionalVector frontDirection = new DirectionalVector(playerHeadRotationLocation);
|
|
||||||
Vector frontHalfVector = frontDirection.toVector().multiply(PLAYER_HEAD_SIZE/2);
|
|
||||||
Vector backHalfDirection = frontDirection.getBackDirection().toVector().multiply(PLAYER_HEAD_SIZE/2);
|
|
||||||
Vector leftHalfVector = frontDirection.getLeftDirection().toVector().multiply(PLAYER_HEAD_SIZE/2);
|
|
||||||
Vector rightHalfVector = frontDirection.getRightDirection().toVector().multiply(PLAYER_HEAD_SIZE/2);
|
|
||||||
Vector topVector = frontDirection.getTopDirection().toVector().multiply(PLAYER_HEAD_SIZE);
|
|
||||||
|
|
||||||
Location bottomFrontMiddle = playerHeadRotationLocation.clone().add(frontHalfVector);
|
|
||||||
Location bottomBackMiddle = playerHeadRotationLocation.clone().add(backHalfDirection);
|
|
||||||
|
|
||||||
Location topFrontMiddle = bottomFrontMiddle.clone().add(topVector);
|
|
||||||
Location topBackMiddle = bottomBackMiddle.clone().add(topVector);
|
|
||||||
|
|
||||||
headAnglesPoints[0] = topFrontMiddle.clone().add(leftHalfVector);
|
|
||||||
headAnglesPoints[1] = topFrontMiddle.clone().add(rightHalfVector);
|
|
||||||
headAnglesPoints[2] = bottomFrontMiddle.clone().add(leftHalfVector);
|
|
||||||
headAnglesPoints[3] = bottomFrontMiddle.clone().add(rightHalfVector);
|
|
||||||
headAnglesPoints[4] = topBackMiddle.clone().add(leftHalfVector);
|
|
||||||
headAnglesPoints[5] = topBackMiddle.clone().add(rightHalfVector);
|
|
||||||
headAnglesPoints[6] = bottomBackMiddle.clone().add(leftHalfVector);
|
|
||||||
headAnglesPoints[7] = bottomBackMiddle.clone().add(rightHalfVector);
|
|
||||||
|
|
||||||
return headAnglesPoints;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Check if the path from <i>start</i> location to <i>end</i> pass through
|
|
||||||
* the axis aligned bounding box defined by <i>min</i> and <i>max</i>.
|
|
||||||
*/
|
|
||||||
public static boolean hasIntersection(Vector start, Vector end, Vector min, Vector max) {
|
|
||||||
final double epsilon = 0.0001f;
|
|
||||||
|
|
||||||
Vector d = end.clone().subtract(start).multiply(0.5);
|
|
||||||
Vector e = max.clone().subtract(min).multiply(0.5);
|
|
||||||
Vector c = start.clone().add(d).subtract(min.clone().add(max).multiply(0.5));
|
|
||||||
Vector ad = d.clone();
|
|
||||||
ad.setX(Math.abs(ad.getX()));
|
|
||||||
ad.setY(Math.abs(ad.getY()));
|
|
||||||
ad.setZ(Math.abs(ad.getZ()));
|
|
||||||
|
|
||||||
return !(
|
|
||||||
Math.abs(c.getX()) > e.getX() + ad.getX()
|
|
||||||
|| Math.abs(c.getY()) > e.getY() + ad.getY()
|
|
||||||
|| Math.abs(c.getZ()) > e.getX() + ad.getZ()
|
|
||||||
|| Math.abs(d.getY() * c.getZ() - d.getZ() * c.getY()) > e.getY() * ad.getZ() + e.getZ() * ad.getY() + epsilon
|
|
||||||
|| Math.abs(d.getZ() * c.getX() - d.getX() * c.getZ()) > e.getZ() * ad.getX() + e.getX() * ad.getZ() + epsilon
|
|
||||||
|| Math.abs(d.getX() * c.getY() - d.getY() * c.getX()) > e.getX() * ad.getY() + e.getY() * ad.getX() + epsilon
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* This vector considers Minecraft X Y Z axis orientation,
|
|
||||||
* but consider standard (not Minecraft) radian values for yaw and pitch.<br/>
|
|
||||||
* The length of this Vector (based on {@link #x}, {@link #y} and {@link #z} values)
|
|
||||||
* Is always 1.
|
|
||||||
*
|
|
||||||
* <pre>Yaw :
|
|
||||||
* North (-z) = -PI/2
|
|
||||||
* East (+x) = 0
|
|
||||||
* South (+z) = PI/2
|
|
||||||
* West (-x) = ±PI
|
|
||||||
*
|
|
||||||
* Pitch :
|
|
||||||
* Up (+y) = PI/2
|
|
||||||
* Down (-y) = -PI/2</pre>
|
|
||||||
*/
|
|
||||||
public static class DirectionalVector {
|
|
||||||
/**
|
|
||||||
* The X cartesian coordinate of this {@link DirectionalVector}.
|
|
||||||
* It corresponds to the X (west to east) axis in a Minecraft world.
|
|
||||||
*/
|
|
||||||
public final double x;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The Y cartesian coordinate of this {@link DirectionalVector}.
|
|
||||||
* It corresponds to the Y (bottom to top) axis in a Minecraft world.
|
|
||||||
*/
|
|
||||||
public final double y;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The Z cartesian coordinate of this {@link DirectionalVector}.
|
|
||||||
* It corresponds to the Z (north to south) axis in a Minecraft world.
|
|
||||||
*/
|
|
||||||
public final double z;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The azimuthal angle φ (phi) of this {@link DirectionalVector}, in radian.
|
|
||||||
* It corresponds with Minecraft world as follows :
|
|
||||||
* <pre>Yaw :
|
|
||||||
* North (-z) = -PI/2
|
|
||||||
* East (+x) = 0
|
|
||||||
* South (+z) = PI/2
|
|
||||||
* West (-x) = ±PI</pre>
|
|
||||||
*/
|
|
||||||
public final double yaw;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The polar angle θ (theta) of this {@link DirectionalVector}, in radian.
|
|
||||||
* It corresponds with Minecraft world as follows :
|
|
||||||
* <pre>Pitch :
|
|
||||||
* Down (-y) = -PI/2
|
|
||||||
* Up (+y) = PI/2</pre>
|
|
||||||
*/
|
|
||||||
public final double pitch;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Initialize this {@link DirectionalVector} with the yaw and pitch
|
|
||||||
* contained in the provided {@link Location}.
|
|
||||||
* {@link Location#getYaw()} and {@link Location#getPitch()} values are automatically
|
|
||||||
* converted to conform {@link #yaw} and {@link #pitch} specification.
|
|
||||||
*/
|
|
||||||
public DirectionalVector(Location l) {
|
|
||||||
this(
|
|
||||||
Math.toRadians(((l.getYaw()+90)%360) > 180 ? ((l.getYaw()+90)%360)-360 : ((l.getYaw()+90)%360)),
|
|
||||||
-Math.toRadians(l.getPitch())
|
|
||||||
);
|
|
||||||
/* MC : +90 : %360 : >180 -> -360
|
|
||||||
* South (+z) = 0, 360 : 90-450 : 90 : 90 : PI/2
|
|
||||||
* West (-x) = 90 : 180 : 180 : ±180 : ±PI
|
|
||||||
* North (-z) = 180 : 270 : 270 : -90 : -PI/2
|
|
||||||
* East (+x) = 270 : 360 : 0-360 : 0 : 0
|
|
||||||
*/
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param v the vector representing the direction. If v.getX() and v.getZ() are 0,
|
|
||||||
* the yaw will be 0. This may have inconsistency if the vector is calculated
|
|
||||||
* from a {@link Location}'s yaw and pitch. In this case, prefer using
|
|
||||||
* {@link #DirectionalVector(Location)}. The {@link Vector} is
|
|
||||||
* normalized if necessary (does not modify the provided {@link Vector}).
|
|
||||||
*/
|
|
||||||
public DirectionalVector(Vector v) {
|
|
||||||
this(v.getX(), v.getY(), v.getZ());
|
|
||||||
// this((v = v.clone().normalize()).getX(), v.getY(), v.getZ());
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
private DirectionalVector(double x, double y, double z) {
|
|
||||||
double vecSize = Math.sqrt(x*x + y*y + z*z);
|
|
||||||
this.x = x/vecSize;
|
|
||||||
this.y = y/vecSize;
|
|
||||||
this.z = z/vecSize;
|
|
||||||
|
|
||||||
if (x == 0.0 && z == 0.0) {
|
|
||||||
pitch = y > 0.0 ? PId2 : -PId2;
|
|
||||||
yaw = 0;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
yaw = Math.atan2(z, x);
|
|
||||||
pitch = Math.atan(y / Math.sqrt(x*x + z*z));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private DirectionalVector(double x, double y, double z, double yaw, double pitch) {
|
|
||||||
this.x = x;
|
|
||||||
this.y = y;
|
|
||||||
this.z = z;
|
|
||||||
this.yaw = yaw;
|
|
||||||
this.pitch = pitch;
|
|
||||||
}
|
|
||||||
|
|
||||||
private DirectionalVector(double yaw, double pitch) {
|
|
||||||
this.yaw = yaw;
|
|
||||||
this.pitch = pitch;
|
|
||||||
|
|
||||||
y = Math.sin(pitch);
|
|
||||||
|
|
||||||
double cosPitch = Math.cos(pitch);
|
|
||||||
x = cosPitch * Math.cos(yaw);
|
|
||||||
z = cosPitch * Math.sin(yaw);
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
public Vector toVector() {
|
|
||||||
return new Vector(x, y, z);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Set the yaw and the pitch of the provided {@link Location}
|
|
||||||
* with the values inside the current {@link DirectionalVector}
|
|
||||||
* after conversion of these values
|
|
||||||
*/
|
|
||||||
public void putIntoLocation(Location l) {
|
|
||||||
/* std : -PI/2 : <0 ? +2PI : MC
|
|
||||||
* South (+z) = PI/2 : 0 : 0 : 0, 360
|
|
||||||
* West (-x) = ±PI : -3PI/2 - PI/2 : PI/2 : 90
|
|
||||||
* North (-z) = -PI/2 : -PI : PI : 180
|
|
||||||
* East (+x) = 0 : -PI/2 : 3PI/2 : 270
|
|
||||||
*/
|
|
||||||
l.setYaw((float)Math.toDegrees(yaw < PId2 ? yaw + PIx2 - PId2 : yaw - PId2));
|
|
||||||
l.setPitch((float)Math.toDegrees(-pitch));
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
public DirectionalVector getOpposite() {
|
|
||||||
return new DirectionalVector(
|
|
||||||
-x,
|
|
||||||
-y,
|
|
||||||
-z,
|
|
||||||
(yaw > 0 ? (yaw - PI) : (yaw + PI)),
|
|
||||||
-pitch
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* If the current direction is the player face direction,
|
|
||||||
* this method return the direction of the back of the head.
|
|
||||||
* This is an alias of {@link #getOpposite()}
|
|
||||||
*/
|
|
||||||
public DirectionalVector getBackDirection() {
|
|
||||||
return getOpposite();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* If the current direction is the player face direction,
|
|
||||||
* this method return the direction of the bottom of the head.
|
|
||||||
*/
|
|
||||||
public DirectionalVector getBottomDirection() {
|
|
||||||
return new DirectionalVector(
|
|
||||||
(pitch > 0 ? yaw : (yaw > 0 ? (yaw - PI) : (yaw + PI))),
|
|
||||||
(pitch > 0 ? (pitch - PId2) : (-PId2 - pitch))
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* If the current direction is the player face direction,
|
|
||||||
* this method return the direction of the top of the head.
|
|
||||||
*/
|
|
||||||
public DirectionalVector getTopDirection() {
|
|
||||||
return new DirectionalVector(
|
|
||||||
(pitch < 0 ? yaw : (yaw > 0 ? (yaw - PI) : (yaw + PI))),
|
|
||||||
(pitch < 0 ? (pitch + PId2) : (PId2 - pitch))
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* If the current direction is the player face direction,
|
|
||||||
* this method return the direction of the left of the head.
|
|
||||||
*/
|
|
||||||
public DirectionalVector getLeftDirection() {
|
|
||||||
return new DirectionalVector(
|
|
||||||
yaw > -PId2 ? (yaw - PId2) : (yaw - PId2 + PIx2),
|
|
||||||
0
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* If the current direction is the player face direction,
|
|
||||||
* this method return the direction of the right of the head.
|
|
||||||
*/
|
|
||||||
public DirectionalVector getRightDirection() {
|
|
||||||
return new DirectionalVector(
|
|
||||||
yaw < PId2 ? (yaw + PId2) : (yaw + PId2 - PIx2),
|
|
||||||
0
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
@ -0,0 +1,70 @@
|
|||||||
|
package fr.pandacube.lib.permissions;
|
||||||
|
|
||||||
|
import fr.pandacube.lib.util.Log;
|
||||||
|
|
||||||
|
import java.util.UUID;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Represents a dummy player in the permission system, that have no specific data, only inheriting from the default
|
||||||
|
* groups.
|
||||||
|
*
|
||||||
|
* The current implementation provides a player named {@code default.0} with an uuid of
|
||||||
|
* {@code fffdef17-ffff-b0ff-ffff-ffffffffffff}.
|
||||||
|
* Trying to set a permission data for this player will log a warning.
|
||||||
|
*/
|
||||||
|
/* package */ final class DefaultPlayer extends PermPlayer {
|
||||||
|
|
||||||
|
|
||||||
|
// a static UUID that ensure it will not collide with any player, either online or offline or floodgate:
|
||||||
|
// the version bits are set to B (11), that is offline mode (3) + alt (8) account, and alt account counter
|
||||||
|
// set to 0 that is usually impossible due to the counter starting at 1.
|
||||||
|
/* package */ static final UUID ID = new UUID(0xfffdef17_ffff_b0ffL, -1L);
|
||||||
|
|
||||||
|
/* package */ static final String NAME = "default.0";
|
||||||
|
|
||||||
|
|
||||||
|
/* package */ DefaultPlayer() {
|
||||||
|
super(ID);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getName() {
|
||||||
|
return NAME;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setGroup(String group) {
|
||||||
|
warnDefaultPlayerSetData();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void addGroup(String group) {
|
||||||
|
warnDefaultPlayerSetData();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void removeGroup(String group) {
|
||||||
|
warnDefaultPlayerSetData();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setSelfPrefix(String prefix) {
|
||||||
|
warnDefaultPlayerSetData();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setSelfSuffix(String suffix) {
|
||||||
|
warnDefaultPlayerSetData();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void addSelfPermission(String permission, String server, String world) {
|
||||||
|
warnDefaultPlayerSetData();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void removeSelfPermission(String permission, String server, String world) {
|
||||||
|
warnDefaultPlayerSetData();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void warnDefaultPlayerSetData() {
|
||||||
|
Log.warning(new UnsupportedOperationException("Trying to set permission data of default player"));
|
||||||
|
}
|
||||||
|
}
|
@ -10,7 +10,7 @@ import fr.pandacube.lib.permissions.SQLPermissions.EntityType;
|
|||||||
/**
|
/**
|
||||||
* Represents a player in the permission system.
|
* Represents a player in the permission system.
|
||||||
*/
|
*/
|
||||||
public final class PermPlayer extends PermEntity {
|
public sealed class PermPlayer extends PermEntity permits DefaultPlayer {
|
||||||
private final UUID playerId;
|
private final UUID playerId;
|
||||||
/* package */ PermPlayer(UUID id) {
|
/* package */ PermPlayer(UUID id) {
|
||||||
super(id.toString(), EntityType.User);
|
super(id.toString(), EntityType.User);
|
||||||
|
@ -104,6 +104,19 @@ public class Permissions {
|
|||||||
return new PermPlayer(playerId);
|
return new PermPlayer(playerId);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets a dummy permission player object, that have no specific data, only inheriting from the default groups.
|
||||||
|
*
|
||||||
|
* The current implementation provides a player named {@code default.0} with an uuid of
|
||||||
|
* {@code fffdef17-ffff-b0ff-ffff-ffffffffffff}.
|
||||||
|
* Trying to set a permission data for this player will log a warning.
|
||||||
|
* @return the default permission player.
|
||||||
|
*/
|
||||||
|
public static PermPlayer getDefaultPlayer() {
|
||||||
|
checkInitialized();
|
||||||
|
return new DefaultPlayer();
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Asks the permission system to preventively and asynchronously cache the data of the provided player.
|
* Asks the permission system to preventively and asynchronously cache the data of the provided player.
|
||||||
* This can be called as soon as possible when a player connects, so the permission data of the player are
|
* This can be called as soon as possible when a player connects, so the permission data of the player are
|
||||||
|
@ -26,6 +26,12 @@ import fr.pandacube.lib.util.Log;
|
|||||||
/* package */ PermissionsCachedBackendReader() throws DBException {
|
/* package */ PermissionsCachedBackendReader() throws DBException {
|
||||||
clearAndResetCache();
|
clearAndResetCache();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* package */ static final CachedPlayer DEFAULT_PLAYER = new CachedPlayer(DefaultPlayer.ID, null, null, Map.of());
|
||||||
|
static {
|
||||||
|
DEFAULT_PLAYER.usingDefaultGroups = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@ -58,6 +64,8 @@ import fr.pandacube.lib.util.Log;
|
|||||||
}
|
}
|
||||||
|
|
||||||
private CachedPlayer initPlayer(UUID playerId) throws DBException {
|
private CachedPlayer initPlayer(UUID playerId) throws DBException {
|
||||||
|
if (playerId.equals(DEFAULT_PLAYER.playerId))
|
||||||
|
return DEFAULT_PLAYER;
|
||||||
|
|
||||||
SQLElementList<SQLPermissions> playerData = DB.getAll(SQLPermissions.class,
|
SQLElementList<SQLPermissions> playerData = DB.getAll(SQLPermissions.class,
|
||||||
SQLPermissions.type.eq(EntityType.User.getCode())
|
SQLPermissions.type.eq(EntityType.User.getCode())
|
||||||
@ -196,6 +204,8 @@ import fr.pandacube.lib.util.Log;
|
|||||||
cacheIsUpdating = false;
|
cacheIsUpdating = false;
|
||||||
usersCache.invalidateAll();
|
usersCache.invalidateAll();
|
||||||
fullPermissionsList = newFullPermissionsList;
|
fullPermissionsList = newFullPermissionsList;
|
||||||
|
DEFAULT_PLAYER.groups.clear();
|
||||||
|
DEFAULT_PLAYER.groups.addAll(getDefaultGroups());
|
||||||
}
|
}
|
||||||
} finally {
|
} finally {
|
||||||
synchronized (this) {
|
synchronized (this) {
|
||||||
|
Loading…
Reference in New Issue
Block a user