Added WorldBorder#isInside + physics now support world border

This commit is contained in:
Felix Cravic 2020-05-27 01:04:39 +02:00
parent 224a8a3b86
commit ef261c1792
6 changed files with 76 additions and 28 deletions

View File

@ -15,7 +15,8 @@ public class CollisionUtils {
/** /**
* Moves an entity with physics applied (ie checking against blocks) * Moves an entity with physics applied (ie checking against blocks)
* @param entity the entity to move *
* @param entity the entity to move
* @param positionOut the Position object in which the new position will be saved * @param positionOut the Position object in which the new position will be saved
* @param velocityOut the Vector object in which the new velocity will be saved * @param velocityOut the Vector object in which the new velocity will be saved
* @return whether this entity is on the ground * @return whether this entity is on the ground
@ -29,7 +30,7 @@ public class CollisionUtils {
boolean yCollision = stepAxis(instance, currentPosition.toVector(), Y_AXIS, deltaPosition.getY(), boolean yCollision = stepAxis(instance, currentPosition.toVector(), Y_AXIS, deltaPosition.getY(),
intermediaryPosition, intermediaryPosition,
deltaPosition.getY() > 0 ? boundingBox.getTopFace() : boundingBox.getBottomFace() deltaPosition.getY() > 0 ? boundingBox.getTopFace() : boundingBox.getBottomFace()
); );
boolean xCollision = stepAxis(instance, intermediaryPosition, X_AXIS, deltaPosition.getX(), boolean xCollision = stepAxis(instance, intermediaryPosition, X_AXIS, deltaPosition.getX(),
intermediaryPosition, intermediaryPosition,
@ -61,12 +62,13 @@ public class CollisionUtils {
/** /**
* Steps on a single axis. Checks against collisions for each point of 'corners'. This method assumes that startPosition is valid. * Steps on a single axis. Checks against collisions for each point of 'corners'. This method assumes that startPosition is valid.
* Immediately return false if corners is of length 0. * Immediately return false if corners is of length 0.
* @param instance instance to check blocks from *
* @param instance instance to check blocks from
* @param startPosition starting position for stepping, can be intermediary position from last step * @param startPosition starting position for stepping, can be intermediary position from last step
* @param axis step direction. Works best if unit vector and aligned to an axis * @param axis step direction. Works best if unit vector and aligned to an axis
* @param stepAmount how much to step in the direction (in blocks) * @param stepAmount how much to step in the direction (in blocks)
* @param positionOut the vector in which to store the new position * @param positionOut the vector in which to store the new position
* @param corners the corners to check against * @param corners the corners to check against
* @return true iif a collision has been found * @return true iif a collision has been found
*/ */
private static boolean stepAxis(Instance instance, Vector startPosition, Vector axis, float stepAmount, Vector positionOut, Vector... corners) { private static boolean stepAxis(Instance instance, Vector startPosition, Vector axis, float stepAmount, Vector positionOut, Vector... corners) {
@ -83,21 +85,21 @@ public class CollisionUtils {
} }
float sign = Math.signum(stepAmount); float sign = Math.signum(stepAmount);
int blockLength = (int)stepAmount; int blockLength = (int) stepAmount;
float remainingLength = stepAmount-blockLength; float remainingLength = stepAmount - blockLength;
// used to determine if 'remainingLength' should be used // used to determine if 'remainingLength' should be used
boolean collisionFound = false; boolean collisionFound = false;
for (int i = 0; i < Math.abs(blockLength); i++) { for (int i = 0; i < Math.abs(blockLength); i++) {
if (!stepOnce(instance, axis, sign, cornersCopy, cornerPositions)) { if (!stepOnce(instance, axis, sign, cornersCopy, cornerPositions)) {
collisionFound = true; collisionFound = true;
} }
if(collisionFound) { if (collisionFound) {
break; break;
} }
} }
// add remainingLength // add remainingLength
if(!collisionFound) { if (!collisionFound) {
Vector direction = new Vector(); Vector direction = new Vector();
direction.copy(axis); direction.copy(axis);
collisionFound |= !stepOnce(instance, direction, remainingLength, cornersCopy, cornerPositions); collisionFound |= !stepOnce(instance, direction, remainingLength, cornersCopy, cornerPositions);
@ -113,15 +115,16 @@ public class CollisionUtils {
} }
positionOut.copy(startPosition); positionOut.copy(startPosition);
positionOut.add(smallestDisplacement*axis.getX()*sign, smallestDisplacement*axis.getY()*sign, smallestDisplacement*axis.getZ()*sign); positionOut.add(smallestDisplacement * axis.getX() * sign, smallestDisplacement * axis.getY() * sign, smallestDisplacement * axis.getZ() * sign);
return collisionFound; return collisionFound;
} }
/** /**
* Steps once (by a length of 1 block) on the given axis. Returns false if this method encountered a collision * Steps once (by a length of 1 block) on the given axis. Returns false if this method encountered a collision
* @param instance instance to get blocks from *
* @param axis the axis to move along * @param instance instance to get blocks from
* @param cornersCopy the corners of the bounding box to consider (mutable) * @param axis the axis to move along
* @param cornersCopy the corners of the bounding box to consider (mutable)
* @param cornerPositions the corners, converted to BlockPosition (mutable) * @param cornerPositions the corners, converted to BlockPosition (mutable)
* @return * @return
*/ */
@ -130,7 +133,7 @@ public class CollisionUtils {
for (int cornerIndex = 0; cornerIndex < cornersCopy.length; cornerIndex++) { for (int cornerIndex = 0; cornerIndex < cornersCopy.length; cornerIndex++) {
Vector corner = cornersCopy[cornerIndex]; Vector corner = cornersCopy[cornerIndex];
BlockPosition blockPos = cornerPositions[cornerIndex]; BlockPosition blockPos = cornerPositions[cornerIndex];
corner.add(axis.getX()*amount, axis.getY()*amount, axis.getZ()*amount); corner.add(axis.getX() * amount, axis.getY() * amount, axis.getZ() * amount);
blockPos.setX((int) Math.floor(corner.getX())); blockPos.setX((int) Math.floor(corner.getX()));
blockPos.setY((int) Math.floor(corner.getY())); blockPos.setY((int) Math.floor(corner.getY()));
blockPos.setZ((int) Math.floor(corner.getZ())); blockPos.setZ((int) Math.floor(corner.getZ()));
@ -140,7 +143,7 @@ public class CollisionUtils {
// TODO: block collision boxes // TODO: block collision boxes
// TODO: for the moment, always consider a full block // TODO: for the moment, always consider a full block
if (block.isSolid()) { if (block.isSolid()) {
corner.subtract(axis.getX()*amount, axis.getY()*amount, axis.getZ()*amount); corner.subtract(axis.getX() * amount, axis.getY() * amount, axis.getZ() * amount);
if (Math.abs(axis.getX()) > 10e-16) { if (Math.abs(axis.getX()) > 10e-16) {
corner.setX(blockPos.getX() - axis.getX() * sign); corner.setX(blockPos.getX() - axis.getX() * sign);

View File

@ -16,6 +16,7 @@ import net.minestom.server.event.entity.EntityVelocityEvent;
import net.minestom.server.event.handler.EventHandler; import net.minestom.server.event.handler.EventHandler;
import net.minestom.server.instance.Chunk; import net.minestom.server.instance.Chunk;
import net.minestom.server.instance.Instance; import net.minestom.server.instance.Instance;
import net.minestom.server.instance.WorldBorder;
import net.minestom.server.instance.block.CustomBlock; import net.minestom.server.instance.block.CustomBlock;
import net.minestom.server.network.packet.PacketWriter; import net.minestom.server.network.packet.PacketWriter;
import net.minestom.server.network.packet.server.play.*; import net.minestom.server.network.packet.server.play.*;
@ -289,8 +290,8 @@ public abstract class Entity implements Viewable, EventHandler, DataContainer {
this.lastUpdate = time; this.lastUpdate = time;
// Velocity // Velocity
boolean applyVelocity = (PlayerUtils.isNettyClient(this) && hasVelocity()) final boolean applyVelocity = !PlayerUtils.isNettyClient(this) ||
|| !PlayerUtils.isNettyClient(this); (PlayerUtils.isNettyClient(this) && hasVelocity());
if (applyVelocity) { if (applyVelocity) {
final float tps = MinecraftServer.TICK_PER_SECOND; final float tps = MinecraftServer.TICK_PER_SECOND;
float newX = position.getX() + velocity.getX() / tps; float newX = position.getX() + velocity.getX() / tps;
@ -299,11 +300,9 @@ public abstract class Entity implements Viewable, EventHandler, DataContainer {
Position newPosition = new Position(newX, newY, newZ); Position newPosition = new Position(newX, newY, newZ);
//if (!PlayerUtils.isNettyClient(this) && !noGravity) { // players handle gravity by themselves
if (!noGravity) { if (!noGravity) {
velocity.setY(velocity.getY() - gravityDragPerTick * tps); velocity.setY(velocity.getY() - gravityDragPerTick * tps);
} }
// }
Vector newVelocityOut = new Vector(); Vector newVelocityOut = new Vector();
Vector deltaPos = new Vector( Vector deltaPos = new Vector(
@ -313,7 +312,15 @@ public abstract class Entity implements Viewable, EventHandler, DataContainer {
); );
onGround = CollisionUtils.handlePhysics(this, deltaPos, newPosition, newVelocityOut); onGround = CollisionUtils.handlePhysics(this, deltaPos, newPosition, newVelocityOut);
refreshPosition(newPosition); WorldBorder worldBorder = instance.getWorldBorder();
if (worldBorder.isInside(newPosition)) {
// Apply velocity + gravity
refreshPosition(newPosition);
} else {
// Only apply Y velocity/gravity
refreshPosition(position.getX(), newPosition.getY(), position.getZ());
}
velocity.copy(newVelocityOut); velocity.copy(newVelocityOut);
velocity.multiply(tps); velocity.multiply(tps);

View File

@ -230,7 +230,7 @@ public abstract class LivingEntity extends Entity implements EquipmentHandler {
// Additional hearts support // Additional hearts support
if (this instanceof Player) { if (this instanceof Player) {
Player player = (Player) this; Player player = (Player) this;
float additionalHearts = player.getAdditionalHearts(); final float additionalHearts = player.getAdditionalHearts();
if (additionalHearts > 0) { if (additionalHearts > 0) {
if (damage > additionalHearts) { if (damage > additionalHearts) {
damage -= additionalHearts; damage -= additionalHearts;

View File

@ -127,7 +127,7 @@ public class Player extends LivingEntity {
private PlayerVehicleInformation vehicleInformation = new PlayerVehicleInformation(); private PlayerVehicleInformation vehicleInformation = new PlayerVehicleInformation();
// Tick related // Tick related
private final PlayerTickEvent playerTickEvent = new PlayerTickEvent(); private final PlayerTickEvent playerTickEvent = new PlayerTickEvent(this);
public Player(UUID uuid, String username, PlayerConnection playerConnection) { public Player(UUID uuid, String username, PlayerConnection playerConnection) {
super(EntityType.PLAYER); super(EntityType.PLAYER);

View File

@ -1,6 +1,17 @@
package net.minestom.server.event.player; package net.minestom.server.event.player;
import net.minestom.server.entity.Player;
import net.minestom.server.event.Event; import net.minestom.server.event.Event;
public class PlayerTickEvent extends Event { public class PlayerTickEvent extends Event {
private Player player;
public PlayerTickEvent(Player player) {
this.player = player;
}
public Player getPlayer() {
return player;
}
} }

View File

@ -1,13 +1,15 @@
package net.minestom.server.instance; package net.minestom.server.instance;
import net.minestom.server.entity.Entity;
import net.minestom.server.entity.Player; import net.minestom.server.entity.Player;
import net.minestom.server.network.packet.server.play.WorldBorderPacket; import net.minestom.server.network.packet.server.play.WorldBorderPacket;
import net.minestom.server.utils.Position;
public class WorldBorder { public class WorldBorder {
private Instance instance; private Instance instance;
private double centerX, centerZ; private float centerX, centerZ;
private volatile double currentDiameter; private volatile double currentDiameter;
@ -33,7 +35,7 @@ public class WorldBorder {
} }
public void setCenter(double centerX, double centerZ) { public void setCenter(float centerX, float centerZ) {
this.centerX = centerX; this.centerX = centerX;
this.centerZ = centerZ; this.centerZ = centerZ;
refreshCenter(); refreshCenter();
@ -43,7 +45,7 @@ public class WorldBorder {
return centerX; return centerX;
} }
public void setCenterX(double centerX) { public void setCenterX(float centerX) {
this.centerX = centerX; this.centerX = centerX;
refreshCenter(); refreshCenter();
} }
@ -52,7 +54,7 @@ public class WorldBorder {
return centerZ; return centerZ;
} }
public void setCenterZ(double centerZ) { public void setCenterZ(float centerZ) {
this.centerZ = centerZ; this.centerZ = centerZ;
refreshCenter(); refreshCenter();
} }
@ -134,6 +136,31 @@ public class WorldBorder {
sendPacket(worldBorderPacket); sendPacket(worldBorderPacket);
} }
/**
* Used to know if a position is located inside the world border or not
*
* @param position the position to check
* @return true if {@code position} is inside the world border, false otherwise
*/
public boolean isInside(Position position) {
final double radius = getDiameter() / 2d;
final boolean checkX = position.getX() < getCenterX() + radius && position.getX() > getCenterX() - radius;
if (!checkX)
return false;
final boolean checkZ = position.getZ() < getCenterZ() + radius && position.getZ() > getCenterZ() - radius;
return checkZ;
}
/**
* Used to know if an entity is located inside the world border or not
*
* @param entity the entity to check
* @return true if {@code entity} is inside the world border, false otherwise
*/
public boolean isInside(Entity entity) {
return isInside(entity.getPosition());
}
/** /**
* Used to update in real-time the current diameter time * Used to update in real-time the current diameter time
*/ */