Movement checks (#2547)

This avoids ArrayList allocations and https://github.com/GeyserMC/Geyser/issues/2540.
This commit is contained in:
David Choo 2021-09-26 10:16:22 -04:00 committed by GitHub
parent 6f4d433561
commit 9a8795988f
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
13 changed files with 199 additions and 198 deletions

View file

@ -27,7 +27,6 @@ package org.geysermc.connector.entity;
import com.github.steveice10.mc.protocol.data.game.entity.metadata.EntityMetadata;
import com.nukkitx.math.vector.Vector3f;
import com.nukkitx.math.vector.Vector3i;
import com.nukkitx.protocol.bedrock.data.entity.EntityData;
import com.nukkitx.protocol.bedrock.data.entity.EntityFlag;
import com.nukkitx.protocol.bedrock.packet.PlaySoundPacket;
@ -35,13 +34,12 @@ import org.geysermc.connector.entity.player.PlayerEntity;
import org.geysermc.connector.entity.type.EntityType;
import org.geysermc.connector.network.session.GeyserSession;
import org.geysermc.connector.network.translators.collision.BoundingBox;
import org.geysermc.connector.network.translators.collision.CollisionManager;
import org.geysermc.connector.network.translators.collision.translators.BlockCollision;
import org.geysermc.connector.network.translators.world.block.BlockStateValues;
import org.geysermc.connector.registry.BlockRegistries;
import org.geysermc.connector.utils.BlockPositionIterator;
import org.geysermc.connector.utils.BlockUtils;
import java.util.List;
import java.util.concurrent.ThreadLocalRandom;
public class FishingHookEntity extends ThrowableEntity {
@ -91,19 +89,16 @@ public class FishingHookEntity extends ThrowableEntity {
boundingBox.setMiddleY(position.getY() + boundingBox.getSizeY() / 2);
boundingBox.setMiddleZ(position.getZ());
CollisionManager collisionManager = session.getCollisionManager();
List<Vector3i> collidableBlocks = collisionManager.getCollidableBlocks(boundingBox);
boolean touchingWater = false;
boolean collided = false;
for (Vector3i blockPos : collidableBlocks) {
int blockID = session.getConnector().getWorldManager().getBlockAt(session, blockPos);
BlockCollision blockCollision = BlockUtils.getCollision(blockID, blockPos);
for (BlockPositionIterator iter = session.getCollisionManager().collidableBlocksIterator(boundingBox); iter.hasNext(); iter.next()) {
int blockID = session.getConnector().getWorldManager().getBlockAt(session, iter.getX(), iter.getY(), iter.getZ());
BlockCollision blockCollision = BlockUtils.getCollision(blockID);
if (blockCollision != null) {
if (blockCollision.checkIntersection(boundingBox)) {
if (blockCollision.checkIntersection(iter.getX(), iter.getY(), iter.getZ(), boundingBox)) {
// TODO Push bounding box out of collision to improve movement
collided = true;
}
blockCollision.reset();
}
int waterLevel = BlockStateValues.getWaterLevel(blockID);
@ -111,10 +106,10 @@ public class FishingHookEntity extends ThrowableEntity {
waterLevel = 0;
}
if (waterLevel >= 0) {
double waterMaxY = blockPos.getY() + 1 - (waterLevel + 1) / 9.0;
double waterMaxY = iter.getY() + 1 - (waterLevel + 1) / 9.0;
// Falling water is a full block
if (waterLevel >= 8) {
waterMaxY = blockPos.getY() + 1;
waterMaxY = iter.getY() + 1;
}
if (position.getY() <= waterMaxY) {
touchingWater = true;

View file

@ -73,7 +73,10 @@ public class BedrockMovePlayerTranslator extends PacketTranslator<MovePlayerPack
// Send book update before the player moves
session.getBookEditCache().checkForSend();
if (!session.getTeleportMap().isEmpty()) {
session.confirmTeleport(packet.getPosition().toDouble().sub(0, EntityType.PLAYER.getOffset(), 0));
return;
}
// head yaw, pitch, head yaw
Vector3f rotation = Vector3f.from(packet.getRotation().getY(), packet.getRotation().getX(), packet.getRotation().getY());
@ -96,7 +99,7 @@ public class BedrockMovePlayerTranslator extends PacketTranslator<MovePlayerPack
return;
}
if (isValidMove(session, packet.getMode(), entity.getPosition(), packet.getPosition())) {
if (isValidMove(session, entity.getPosition(), packet.getPosition())) {
Vector3d position = session.getCollisionManager().adjustBedrockPosition(packet.getPosition(), packet.isOnGround(), packet.getMode() == MovePlayerPacket.Mode.TELEPORT);
if (position != null) { // A null return value cancels the packet
Packet movePacket;
@ -155,22 +158,15 @@ public class BedrockMovePlayerTranslator extends PacketTranslator<MovePlayerPack
}
}
private boolean isValidMove(GeyserSession session, MovePlayerPacket.Mode mode, Vector3f currentPosition, Vector3f newPosition) {
if (mode != MovePlayerPacket.Mode.NORMAL)
return true;
private boolean isInvalidNumber(float val) {
return Float.isNaN(val) || Float.isInfinite(val);
}
double xRange = newPosition.getX() - currentPosition.getX();
double yRange = newPosition.getY() - currentPosition.getY();
double zRange = newPosition.getZ() - currentPosition.getZ();
if (xRange < 0)
xRange = -xRange;
if (yRange < 0)
yRange = -yRange;
if (zRange < 0)
zRange = -zRange;
if ((xRange + yRange + zRange) > 100) {
private boolean isValidMove(GeyserSession session, Vector3f currentPosition, Vector3f newPosition) {
if (isInvalidNumber(newPosition.getX()) || isInvalidNumber(newPosition.getY()) || isInvalidNumber(newPosition.getZ())) {
return false;
}
if (currentPosition.distanceSquared(newPosition) > 300) {
session.getConnector().getLogger().debug(ChatColor.RED + session.getName() + " moved too quickly." +
" current position: " + currentPosition + ", new position: " + newPosition);

View file

@ -89,39 +89,41 @@ public class BoundingBox implements Cloneable {
return Vector3d.from(middleX, middleY - sizeY / 2, middleZ);
}
private boolean checkOverlapInAxis(Vector3d offset, BoundingBox otherBox, Axis axis) {
private boolean checkOverlapInAxis(double xOffset, double yOffset, double zOffset, BoundingBox otherBox, Axis axis) {
return switch (axis) {
case X -> Math.abs((middleX + offset.getX()) - otherBox.getMiddleX()) * 2 < (sizeX + otherBox.getSizeX());
case Y -> Math.abs((middleY + offset.getY()) - otherBox.getMiddleY()) * 2 < (sizeY + otherBox.getSizeY());
case Z -> Math.abs((middleZ + offset.getZ()) - otherBox.getMiddleZ()) * 2 < (sizeZ + otherBox.getSizeZ());
case X -> Math.abs((middleX + xOffset) - otherBox.getMiddleX()) * 2 < (sizeX + otherBox.getSizeX());
case Y -> Math.abs((middleY + yOffset) - otherBox.getMiddleY()) * 2 < (sizeY + otherBox.getSizeY());
case Z -> Math.abs((middleZ + zOffset) - otherBox.getMiddleZ()) * 2 < (sizeZ + otherBox.getSizeZ());
};
}
/**
* Find the maximum offset of another bounding box in an axis that will not collide with this bounding box
*
* @param boxOffset The offset of this bounding box
* @param xOffset The x offset of this bounding box
* @param yOffset The y offset of this bounding box
* @param zOffset The z offset of this bounding box
* @param otherBoundingBox The bounding box that is moving
* @param axis The axis of movement
* @param offset The current max offset
* @return The new max offset
*/
public double getMaxOffset(Vector3d boxOffset, BoundingBox otherBoundingBox, Axis axis, double offset) {
public double getMaxOffset(double xOffset, double yOffset, double zOffset, BoundingBox otherBoundingBox, Axis axis, double offset) {
// Make sure that the bounding box overlaps in the other axes
for (Axis a : Axis.VALUES) {
if (a != axis && !checkOverlapInAxis(boxOffset, otherBoundingBox, a)) {
if (a != axis && !checkOverlapInAxis(xOffset, yOffset, zOffset, otherBoundingBox, a)) {
return offset;
}
}
if (offset > 0) {
double min = axis.choose(getMin().add(boxOffset));
double min = axis.choose(getMin().add(xOffset, yOffset, zOffset));
double max = axis.choose(otherBoundingBox.getMax());
if ((min - max) >= -2.0 * CollisionManager.COLLISION_TOLERANCE) {
offset = Math.min(min - max, offset);
}
} else if (offset < 0) {
double min = axis.choose(otherBoundingBox.getMin());
double max = axis.choose(getMax().add(boxOffset));
double max = axis.choose(getMax().add(xOffset, yOffset, zOffset));
if ((min - max) >= -2.0 * CollisionManager.COLLISION_TOLERANCE) {
offset = Math.max(max - min, offset);
}

View file

@ -42,13 +42,12 @@ import org.geysermc.connector.network.session.cache.PistonCache;
import org.geysermc.connector.network.translators.collision.translators.BlockCollision;
import org.geysermc.connector.network.translators.collision.translators.ScaffoldingCollision;
import org.geysermc.connector.network.translators.world.block.BlockStateValues;
import org.geysermc.connector.utils.BlockPositionIterator;
import org.geysermc.connector.utils.BlockUtils;
import org.geysermc.connector.utils.Axis;
import java.text.DecimalFormat;
import java.text.DecimalFormatSymbols;
import java.util.ArrayList;
import java.util.List;
import java.util.Locale;
public class CollisionManager {
@ -133,6 +132,7 @@ public class CollisionManager {
*
* @param bedrockPosition the current Bedrock position of the client
* @param onGround whether the Bedrock player is on the ground
* @param teleported whether the Bedrock player has teleported to a new position. If true, movement correction is skipped.
* @return the position to send to the Java server, or null to cancel sending the packet
*/
public Vector3d adjustBedrockPosition(Vector3f bedrockPosition, boolean onGround, boolean teleported) {
@ -201,9 +201,7 @@ public class CollisionManager {
session.sendUpstreamPacket(movePlayerPacket);
}
public List<Vector3i> getCollidableBlocks(BoundingBox box) {
List<Vector3i> blocks = new ArrayList<>();
public BlockPositionIterator collidableBlocksIterator(BoundingBox box) {
Vector3d position = Vector3d.from(box.getMiddleX(),
box.getMiddleY() - (box.getSizeY() / 2),
box.getMiddleZ());
@ -211,42 +209,28 @@ public class CollisionManager {
// Expand volume by 1 in each direction to include moving blocks
double pistonExpand = session.getPistonCache().getPistons().isEmpty() ? 0 : 1;
// Ensure sizes cannot be too large - https://github.com/GeyserMC/Geyser/issues/2540
double sizeX = Math.min(box.getSizeX(), 256);
double sizeY = Math.min(box.getSizeY(), 256);
double sizeZ = Math.min(box.getSizeZ(), 256);
// Loop through all blocks that could collide
int minCollisionX = (int) Math.floor(position.getX() - ((sizeX / 2) + COLLISION_TOLERANCE + pistonExpand));
int maxCollisionX = (int) Math.floor(position.getX() + (sizeX / 2) + COLLISION_TOLERANCE + pistonExpand);
int minCollisionX = (int) Math.floor(position.getX() - ((box.getSizeX() / 2) + COLLISION_TOLERANCE + pistonExpand));
int maxCollisionX = (int) Math.floor(position.getX() + (box.getSizeX() / 2) + COLLISION_TOLERANCE + pistonExpand);
// Y extends 0.5 blocks down because of fence hitboxes
int minCollisionY = (int) Math.floor(position.getY() - 0.5 - COLLISION_TOLERANCE - pistonExpand / 2.0);
int maxCollisionY = (int) Math.floor(position.getY() + box.getSizeY() + pistonExpand);
int maxCollisionY = (int) Math.floor(position.getY() + sizeY + pistonExpand);
int minCollisionZ = (int) Math.floor(position.getZ() - ((box.getSizeZ() / 2) + COLLISION_TOLERANCE + pistonExpand));
int maxCollisionZ = (int) Math.floor(position.getZ() + (box.getSizeZ() / 2) + COLLISION_TOLERANCE + pistonExpand);
int minCollisionZ = (int) Math.floor(position.getZ() - ((sizeZ / 2) + COLLISION_TOLERANCE + pistonExpand));
int maxCollisionZ = (int) Math.floor(position.getZ() + (sizeZ / 2) + COLLISION_TOLERANCE + pistonExpand);
for (int y = minCollisionY; y < maxCollisionY + 1; y++) {
for (int x = minCollisionX; x < maxCollisionX + 1; x++) {
for (int z = minCollisionZ; z < maxCollisionZ + 1; z++) {
blocks.add(Vector3i.from(x, y, z));
}
}
return new BlockPositionIterator(minCollisionX, minCollisionY, minCollisionZ, maxCollisionX, maxCollisionY, maxCollisionZ);
}
return blocks;
}
public List<Vector3i> getPlayerCollidableBlocks() {
return getCollidableBlocks(playerBoundingBox);
public BlockPositionIterator playerCollidableBlocksIterator() {
return collidableBlocksIterator(playerBoundingBox);
}
/**
* Returns false if the movement is invalid, and in this case it shouldn't be sent to the server and should be
* cancelled
* See {@link BlockCollision#correctPosition(GeyserSession, BoundingBox)} for more info
* See {@link BlockCollision#correctPosition(GeyserSession, int, int, int, BoundingBox)} for more info
*/
public boolean correctPlayerPosition() {
@ -254,25 +238,22 @@ public class CollisionManager {
touchingScaffolding = false;
onScaffolding = false;
List<Vector3i> collidableBlocks = getPlayerCollidableBlocks();
// Used when correction code needs to be run before the main correction
for (Vector3i blockPos : collidableBlocks) {
BlockCollision blockCollision = BlockUtils.getCollisionAt(session, blockPos);
BlockPositionIterator iter = session.getCollisionManager().playerCollidableBlocksIterator();
for (; iter.hasNext(); iter.next()) {
BlockCollision blockCollision = BlockUtils.getCollisionAt(session, iter.getX(), iter.getY(), iter.getZ());
if (blockCollision != null) {
blockCollision.beforeCorrectPosition(playerBoundingBox);
blockCollision.reset();
blockCollision.beforeCorrectPosition(iter.getX(), iter.getY(), iter.getZ(), playerBoundingBox);
}
}
// Main correction code
for (Vector3i blockPos : collidableBlocks) {
BlockCollision blockCollision = BlockUtils.getCollisionAt(session, blockPos);
for (iter.reset(); iter.hasNext(); iter.next()) {
BlockCollision blockCollision = BlockUtils.getCollisionAt(session, iter.getX(), iter.getY(), iter.getZ());
if (blockCollision != null) {
if (!blockCollision.correctPosition(session, playerBoundingBox)) {
if (!blockCollision.correctPosition(session, iter.getX(), iter.getY(), iter.getZ(), playerBoundingBox)) {
return false;
}
blockCollision.reset();
}
}
@ -341,24 +322,22 @@ public class CollisionManager {
BoundingBox movementBoundingBox = boundingBox.clone();
movementBoundingBox.extend(movement);
List<Vector3i> collidableBlocks = getCollidableBlocks(movementBoundingBox);
BlockPositionIterator iter = collidableBlocksIterator(movementBoundingBox);
if (Math.abs(movementY) > CollisionManager.COLLISION_TOLERANCE) {
movementY = computeCollisionOffset(boundingBox, Axis.Y, movementY, collidableBlocks, checkWorld);
movementY = computeCollisionOffset(boundingBox, Axis.Y, movementY, iter, checkWorld);
boundingBox.translate(0, movementY, 0);
}
boolean checkZFirst = Math.abs(movementZ) > Math.abs(movementX);
if (checkZFirst && Math.abs(movementZ) > CollisionManager.COLLISION_TOLERANCE) {
movementZ = computeCollisionOffset(boundingBox, Axis.Z, movementZ, collidableBlocks, checkWorld);
movementZ = computeCollisionOffset(boundingBox, Axis.Z, movementZ, iter, checkWorld);
boundingBox.translate(0, 0, movementZ);
}
if (Math.abs(movementX) > CollisionManager.COLLISION_TOLERANCE) {
movementX = computeCollisionOffset(boundingBox, Axis.X, movementX, collidableBlocks, checkWorld);
movementX = computeCollisionOffset(boundingBox, Axis.X, movementX, iter, checkWorld);
boundingBox.translate(movementX, 0, 0);
}
if (!checkZFirst && Math.abs(movementZ) > CollisionManager.COLLISION_TOLERANCE) {
movementZ = computeCollisionOffset(boundingBox, Axis.Z, movementZ, collidableBlocks, checkWorld);
movementZ = computeCollisionOffset(boundingBox, Axis.Z, movementZ, iter, checkWorld);
boundingBox.translate(0, 0, movementZ);
}
@ -366,16 +345,18 @@ public class CollisionManager {
return Vector3d.from(movementX, movementY, movementZ);
}
private double computeCollisionOffset(BoundingBox boundingBox, Axis axis, double offset, List<Vector3i> collidableBlocks, boolean checkWorld) {
for (Vector3i blockPos : collidableBlocks) {
private double computeCollisionOffset(BoundingBox boundingBox, Axis axis, double offset, BlockPositionIterator iter, boolean checkWorld) {
for (iter.reset(); iter.hasNext(); iter.next()) {
int x = iter.getX();
int y = iter.getY();
int z = iter.getZ();
if (checkWorld) {
BlockCollision blockCollision = BlockUtils.getCollisionAt(session, blockPos);
BlockCollision blockCollision = BlockUtils.getCollisionAt(session, x, y, z);
if (blockCollision != null && !(blockCollision instanceof ScaffoldingCollision)) {
offset = blockCollision.computeCollisionOffset(boundingBox, axis, offset);
blockCollision.reset();
offset = blockCollision.computeCollisionOffset(x, y, z, boundingBox, axis, offset);
}
}
offset = session.getPistonCache().computeCollisionOffset(blockPos, boundingBox, axis, offset);
offset = session.getPistonCache().computeCollisionOffset(Vector3i.from(x, y, z), boundingBox, axis, offset);
if (Math.abs(offset) < COLLISION_TOLERANCE) {
return 0;
}
@ -399,9 +380,8 @@ public class CollisionManager {
playerBoundingBox.setSizeY(EntityType.PLAYER.getHeight());
playerBoundingBox.setMiddleY(standingY);
boolean result = collision.checkIntersection(playerBoundingBox);
boolean result = collision.checkIntersection(position, playerBoundingBox);
result |= session.getPistonCache().checkCollision(position, playerBoundingBox);
collision.reset();
playerBoundingBox.setSizeY(originalHeight);
playerBoundingBox.setMiddleY(originalY);
return result;

View file

@ -40,16 +40,6 @@ public class BlockCollision {
@Getter
protected final BoundingBox[] boundingBoxes;
@EqualsAndHashCode.Exclude
protected final ThreadLocal<Vector3i> position;
/**
* Store a Vector3d to allow the collision to be offset by a fractional amount
* This is used only in {@link #checkIntersection(BoundingBox)} and {@link #computeCollisionOffset(BoundingBox, Axis, double)}
*/
@EqualsAndHashCode.Exclude
protected final ThreadLocal<Vector3d> positionOffset;
/**
* This is used for the step up logic.
* Usually, the player can only step up a block if they are on the same Y level as its bottom face or higher
@ -68,28 +58,13 @@ public class BlockCollision {
protected BlockCollision(BoundingBox[] boxes) {
this.boundingBoxes = boxes;
this.position = new ThreadLocal<>();
this.positionOffset = new ThreadLocal<>();
}
public void setPosition(Vector3i newPosition) {
this.position.set(newPosition);
}
public void setPositionOffset(Vector3d newOffset) {
this.positionOffset.set(newOffset);
}
public void reset() {
this.position.set(null);
this.positionOffset.set(null);
}
/**
* Overridden in classes like SnowCollision and GrassPathCollision when correction code needs to be run before the
* main correction
*/
public void beforeCorrectPosition(BoundingBox playerCollision) {}
public void beforeCorrectPosition(int x, int y, int z, BoundingBox playerCollision) {}
/**
* Returns false if the movement is invalid, and in this case it shouldn't be sent to the server and should be
@ -97,12 +72,7 @@ public class BlockCollision {
* While the Java server should do this, it could result in false flags by anticheat
* This functionality is currently only used in 6 or 7 layer snow
*/
public boolean correctPosition(GeyserSession session, BoundingBox playerCollision) {
Vector3i blockPos = this.position.get();
int x = blockPos.getX();
int y = blockPos.getY();
int z = blockPos.getZ();
public boolean correctPosition(GeyserSession session, int x, int y, int z, BoundingBox playerCollision) {
double playerMinY = playerCollision.getMiddleY() - (playerCollision.getSizeY() / 2);
for (BoundingBox b : this.boundingBoxes) {
double boxMinY = (b.getMiddleY() + y) - (b.getSizeY() / 2);
@ -174,29 +144,22 @@ public class BlockCollision {
return true;
}
private Vector3d getFullPos() {
Vector3i blockPos = this.position.get();
Vector3d blockOffset = this.positionOffset.get();
if (blockOffset != null && blockOffset != Vector3d.ZERO) {
return blockOffset.add(blockPos.getX(), blockPos.getY(), blockPos.getZ());
}
return blockPos.toDouble();
}
public boolean checkIntersection(BoundingBox playerCollision) {
Vector3d blockPos = getFullPos();
public boolean checkIntersection(double x, double y, double z, BoundingBox playerCollision) {
for (BoundingBox b : boundingBoxes) {
if (b.checkIntersection(blockPos, playerCollision)) {
if (b.checkIntersection(x, y, z, playerCollision)) {
return true;
}
}
return false;
}
public double computeCollisionOffset(BoundingBox boundingBox, Axis axis, double offset) {
Vector3d blockPos = getFullPos();
public boolean checkIntersection(Vector3i position, BoundingBox playerCollision) {
return checkIntersection(position.getX(), position.getY(), position.getZ(), playerCollision);
}
public double computeCollisionOffset(double x, double y, double z, BoundingBox boundingBox, Axis axis, double offset) {
for (BoundingBox b : boundingBoxes) {
offset = b.getMaxOffset(blockPos, boundingBox, axis, offset);
offset = b.getMaxOffset(x, y, z, boundingBox, axis, offset);
if (Math.abs(offset) < CollisionManager.COLLISION_TOLERANCE) {
return 0;
}

View file

@ -40,10 +40,10 @@ public class DirtPathCollision extends BlockCollision {
// Needs to run before the main correction code or it can move the player into blocks
// This is counteracted by the main collision code pushing them out
@Override
public void beforeCorrectPosition(BoundingBox playerCollision) {
public void beforeCorrectPosition(int x, int y, int z, BoundingBox playerCollision) {
// In Bedrock, dirt paths are solid blocks, so the player must be pushed down.
double playerMinY = playerCollision.getMiddleY() - (playerCollision.getSizeY() / 2);
double blockMaxY = position.get().getY() + 1;
double blockMaxY = y + 1;
if (Math.abs(blockMaxY - playerMinY) <= CollisionManager.COLLISION_TOLERANCE) {
playerCollision.translate(0, -0.0625, 0);
}

View file

@ -61,19 +61,15 @@ public class DoorCollision extends BlockCollision {
}
@Override
public boolean correctPosition(GeyserSession session, BoundingBox playerCollision) {
boolean result = super.correctPosition(session, playerCollision);
public boolean correctPosition(GeyserSession session, int x, int y, int z, BoundingBox playerCollision) {
boolean result = super.correctPosition(session, x, y, z, playerCollision);
// Hack to prevent false positives
playerCollision.setSizeX(playerCollision.getSizeX() - 0.0001);
playerCollision.setSizeY(playerCollision.getSizeY() - 0.0001);
playerCollision.setSizeZ(playerCollision.getSizeZ() - 0.0001);
// Check for door bug (doors are 0.1875 blocks thick on Java but 0.1825 blocks thick on Bedrock)
if (this.checkIntersection(playerCollision)) {
Vector3i blockPos = this.position.get();
int x = blockPos.getX();
int z = blockPos.getZ();
if (this.checkIntersection(x, y, z, playerCollision)) {
switch (facing) {
case 1 -> playerCollision.setMiddleZ(z + 0.5125); // North
case 2 -> playerCollision.setMiddleX(x + 0.5125); // East

View file

@ -41,12 +41,12 @@ public class ScaffoldingCollision extends BlockCollision {
}
@Override
public boolean correctPosition(GeyserSession session, BoundingBox playerCollision) {
public boolean correctPosition(GeyserSession session, int x, int y, int z, BoundingBox playerCollision) {
// Hack to not check below the player
playerCollision.setSizeY(playerCollision.getSizeY() - 0.001);
playerCollision.setMiddleY(playerCollision.getMiddleY() + 0.002);
boolean intersected = this.checkIntersection(playerCollision);
boolean intersected = this.checkIntersection(x, y, z, playerCollision);
playerCollision.setSizeY(playerCollision.getSizeY() + 0.001);
playerCollision.setMiddleY(playerCollision.getMiddleY() - 0.002);
@ -59,7 +59,7 @@ public class ScaffoldingCollision extends BlockCollision {
playerCollision.setSizeY(playerCollision.getSizeY() + 0.001);
playerCollision.setMiddleY(playerCollision.getMiddleY() - 0.002);
if (this.checkIntersection(playerCollision)) {
if (this.checkIntersection(x, y, z, playerCollision)) {
session.getCollisionManager().setOnScaffolding(true);
}

View file

@ -46,13 +46,13 @@ public class SnowCollision extends BlockCollision {
// Needs to run before the main correction code or it can move the player into blocks
// This is counteracted by the main collision code pushing them out
@Override
public void beforeCorrectPosition(BoundingBox playerCollision) {
public void beforeCorrectPosition(int x, int y, int z, BoundingBox playerCollision) {
// In Bedrock, snow layers round down to half blocks but you can't sink into them at all
// This means the collision each half block reaches above where it should be on Java so the player has to be
// pushed down
if (layers == 4 || layers == 8) {
double playerMinY = playerCollision.getMiddleY() - (playerCollision.getSizeY() / 2);
double boxMaxY = (boundingBoxes[0].getMiddleY() + position.get().getY()) + (boundingBoxes[0].getSizeY() / 2);
double boxMaxY = (boundingBoxes[0].getMiddleY() + y) + (boundingBoxes[0].getSizeY() / 2);
// If the player is in the buggy area, push them down
if (playerMinY > boxMaxY &&
playerMinY <= (boxMaxY + 0.125)) {
@ -62,7 +62,7 @@ public class SnowCollision extends BlockCollision {
}
@Override
public boolean correctPosition(GeyserSession session, BoundingBox playerCollision) {
public boolean correctPosition(GeyserSession session, int x, int y, int z, BoundingBox playerCollision) {
if (layers == 1) {
// 1 layer of snow does not have collision
return true;
@ -72,9 +72,9 @@ public class SnowCollision extends BlockCollision {
playerCollision.setSizeY(playerCollision.getSizeY() - 0.0001);
playerCollision.setSizeZ(playerCollision.getSizeZ() - 0.0001);
if (this.checkIntersection(playerCollision)) {
if (this.checkIntersection(x, y, z, playerCollision)) {
double playerMinY = playerCollision.getMiddleY() - (playerCollision.getSizeY() / 2);
double boxMaxY = (boundingBoxes[0].getMiddleY() + position.get().getY()) + (boundingBoxes[0].getSizeY() / 2);
double boxMaxY = (boundingBoxes[0].getMiddleY() + y) + (boundingBoxes[0].getSizeY() / 2);
// If the player actually can't step onto it (they can step onto it from other snow layers)
if ((boxMaxY - playerMinY) > 0.5) {
// Cancel the movement
@ -85,6 +85,6 @@ public class SnowCollision extends BlockCollision {
playerCollision.setSizeX(playerCollision.getSizeX() + 0.0001);
playerCollision.setSizeY(playerCollision.getSizeY() + 0.0001);
playerCollision.setSizeZ(playerCollision.getSizeZ() + 0.0001);
return super.correctPosition(session, playerCollision);
return super.correctPosition(session, x, y, z, playerCollision);
}
}

View file

@ -25,7 +25,6 @@
package org.geysermc.connector.network.translators.collision.translators;
import com.nukkitx.math.vector.Vector3i;
import lombok.EqualsAndHashCode;
import org.geysermc.connector.network.session.GeyserSession;
import org.geysermc.connector.network.translators.collision.BoundingBox;
@ -69,15 +68,10 @@ public class TrapdoorCollision extends BlockCollision {
}
@Override
public boolean correctPosition(GeyserSession session, BoundingBox playerCollision) {
boolean result = super.correctPosition(session, playerCollision);
public boolean correctPosition(GeyserSession session, int x, int y, int z, BoundingBox playerCollision) {
boolean result = super.correctPosition(session, x, y, z, playerCollision);
// Check for door bug (doors are 0.1875 blocks thick on Java but 0.1825 blocks thick on Bedrock)
if (this.checkIntersection(playerCollision)) {
Vector3i blockPos = this.position.get();
int x = blockPos.getX();
int y = blockPos.getY();
int z = blockPos.getZ();
if (this.checkIntersection(x, y, z, playerCollision)) {
switch (facing) {
case 1: // North
playerCollision.setMiddleZ(z + 0.5125);

View file

@ -526,16 +526,7 @@ public class PistonBlockEntity {
}
private BlockCollision getCollision(Vector3i blockPos) {
int blockId = getAttachedBlockId(blockPos);
if (blockId != BlockStateValues.JAVA_AIR_ID) {
double movementProgress = progress;
if (action == PistonValueType.PULLING || action == PistonValueType.CANCELLED_MID_PUSH) {
movementProgress = 1f - progress;
}
Vector3d offset = getMovement().toDouble().mul(movementProgress);
return BlockUtils.getCollision(blockId, blockPos, offset);
}
return null;
return BlockUtils.getCollision(getAttachedBlockId(blockPos));
}
/**
@ -550,8 +541,15 @@ public class PistonBlockEntity {
public double computeCollisionOffset(Vector3i blockPos, BoundingBox boundingBox, Axis axis, double movement) {
BlockCollision blockCollision = getCollision(blockPos);
if (blockCollision != null) {
double adjustedMovement = blockCollision.computeCollisionOffset(boundingBox, axis, movement);
blockCollision.reset();
double movementProgress = progress;
if (action == PistonValueType.PULLING || action == PistonValueType.CANCELLED_MID_PUSH) {
movementProgress = 1f - progress;
}
Vector3i movementVec = getMovement();
double x = blockPos.getX() + movementVec.getX() * movementProgress;
double y = blockPos.getY() + movementVec.getY() * movementProgress;
double z = blockPos.getZ() + movementVec.getZ() * movementProgress;
double adjustedMovement = blockCollision.computeCollisionOffset(x, y, z, boundingBox, axis, movement);
if (getAttachedBlockId(blockPos) == BlockStateValues.JAVA_SLIME_BLOCK_ID && adjustedMovement != movement) {
session.getPistonCache().setPlayerSlimeCollision(true);
}
@ -563,9 +561,15 @@ public class PistonBlockEntity {
public boolean checkCollision(Vector3i blockPos, BoundingBox boundingBox) {
BlockCollision blockCollision = getCollision(blockPos);
if (blockCollision != null) {
boolean result = blockCollision.checkIntersection(boundingBox);
blockCollision.reset();
return result;
double movementProgress = progress;
if (action == PistonValueType.PULLING || action == PistonValueType.CANCELLED_MID_PUSH) {
movementProgress = 1f - progress;
}
Vector3i movementVec = getMovement();
double x = blockPos.getX() + movementVec.getX() * movementProgress;
double y = blockPos.getY() + movementVec.getY() * movementProgress;
double z = blockPos.getZ() + movementVec.getZ() * movementProgress;
return blockCollision.checkIntersection(x, y, z, boundingBox);
}
return false;
}

View file

@ -0,0 +1,82 @@
/*
* Copyright (c) 2019-2021 GeyserMC. http://geysermc.org
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*
* @author GeyserMC
* @link https://github.com/GeyserMC/Geyser
*/
package org.geysermc.connector.utils;
import com.nukkitx.network.util.Preconditions;
import lombok.Getter;
public class BlockPositionIterator {
private final int minX;
private final int minY;
private final int minZ;
private final int sizeX;
private final int sizeZ;
private int i = 0;
private final int maxI;
public BlockPositionIterator(int minX, int minY, int minZ, int maxX, int maxY, int maxZ) {
Preconditions.checkArgument(maxX >= minX, "maxX is not greater than or equal to minX");
Preconditions.checkArgument(maxY >= minY, "maxY is not greater than or equal to minY");
Preconditions.checkArgument(maxZ >= minZ, "maxZ is not greater than or equal to minZ");
this.minX = minX;
this.minY = minY;
this.minZ = minZ;
this.sizeX = maxX - minX + 1;
int sizeY = maxY - minY + 1;
this.sizeZ = maxZ - minZ + 1;
this.maxI = sizeX * sizeY * sizeZ;
}
public boolean hasNext() {
return i < maxI;
}
public void next() {
// Iterate in zxy order
i++;
}
public void reset() {
i = 0;
}
public int getX() {
return ((i / sizeZ) % sizeX) + minX;
}
public int getY() {
return (i / sizeZ / sizeX) + minY;
}
public int getZ() {
return (i % sizeZ) + minZ;
}
}

View file

@ -27,7 +27,6 @@ package org.geysermc.connector.utils;
import com.github.steveice10.mc.protocol.data.game.entity.metadata.Position;
import com.github.steveice10.opennbt.tag.builtin.CompoundTag;
import com.nukkitx.math.vector.Vector3d;
import com.nukkitx.math.vector.Vector3i;
import org.geysermc.connector.inventory.GeyserItemStack;
import org.geysermc.connector.inventory.PlayerInventory;
@ -231,25 +230,15 @@ public class BlockUtils {
return fullJavaIdentifier.substring(0, stateIndex);
}
public static BlockCollision getCollision(int blockId, Vector3i blockPos) {
BlockCollision collision = Registries.COLLISIONS.get(blockId);
if (collision != null) {
collision.setPosition(blockPos);
collision.setPositionOffset(null);
}
return collision;
}
public static BlockCollision getCollision(int blockId, Vector3i blockPos, Vector3d blockOffset) {
BlockCollision collision = Registries.COLLISIONS.get(blockId);
if (collision != null) {
collision.setPosition(blockPos);
collision.setPositionOffset(blockOffset);
}
return collision;
public static BlockCollision getCollision(int blockId) {
return Registries.COLLISIONS.get(blockId);
}
public static BlockCollision getCollisionAt(GeyserSession session, Vector3i blockPos) {
return getCollision(session.getConnector().getWorldManager().getBlockAt(session, blockPos), blockPos);
return getCollision(session.getConnector().getWorldManager().getBlockAt(session, blockPos));
}
public static BlockCollision getCollisionAt(GeyserSession session, int x, int y, int z) {
return getCollision(session.getConnector().getWorldManager().getBlockAt(session, x, y, z));
}
}