From 9a8795988fff4b05fa771121bafee00971f5ab5a Mon Sep 17 00:00:00 2001 From: David Choo Date: Sun, 26 Sep 2021 10:16:22 -0400 Subject: [PATCH] Movement checks (#2547) This avoids ArrayList allocations and https://github.com/GeyserMC/Geyser/issues/2540. --- .../connector/entity/FishingHookEntity.java | 19 ++-- .../player/BedrockMovePlayerTranslator.java | 30 +++---- .../translators/collision/BoundingBox.java | 20 +++-- .../collision/CollisionManager.java | 86 +++++++------------ .../collision/translators/BlockCollision.java | 57 +++--------- .../translators/DirtPathCollision.java | 4 +- .../collision/translators/DoorCollision.java | 10 +-- .../translators/ScaffoldingCollision.java | 6 +- .../collision/translators/SnowCollision.java | 12 +-- .../translators/TrapdoorCollision.java | 12 +-- .../world/block/entity/PistonBlockEntity.java | 34 ++++---- .../utils/BlockPositionIterator.java | 82 ++++++++++++++++++ .../geysermc/connector/utils/BlockUtils.java | 25 ++---- 13 files changed, 199 insertions(+), 198 deletions(-) create mode 100644 connector/src/main/java/org/geysermc/connector/utils/BlockPositionIterator.java diff --git a/connector/src/main/java/org/geysermc/connector/entity/FishingHookEntity.java b/connector/src/main/java/org/geysermc/connector/entity/FishingHookEntity.java index 91c3d401a..565614143 100644 --- a/connector/src/main/java/org/geysermc/connector/entity/FishingHookEntity.java +++ b/connector/src/main/java/org/geysermc/connector/entity/FishingHookEntity.java @@ -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 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; diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/bedrock/entity/player/BedrockMovePlayerTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/bedrock/entity/player/BedrockMovePlayerTranslator.java index 869af9ae6..f70233c04 100644 --- a/connector/src/main/java/org/geysermc/connector/network/translators/bedrock/entity/player/BedrockMovePlayerTranslator.java +++ b/connector/src/main/java/org/geysermc/connector/network/translators/bedrock/entity/player/BedrockMovePlayerTranslator.java @@ -73,7 +73,10 @@ public class BedrockMovePlayerTranslator extends PacketTranslator 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); diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/collision/BoundingBox.java b/connector/src/main/java/org/geysermc/connector/network/translators/collision/BoundingBox.java index 27c200a99..e74953b7a 100644 --- a/connector/src/main/java/org/geysermc/connector/network/translators/collision/BoundingBox.java +++ b/connector/src/main/java/org/geysermc/connector/network/translators/collision/BoundingBox.java @@ -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); } diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/collision/CollisionManager.java b/connector/src/main/java/org/geysermc/connector/network/translators/collision/CollisionManager.java index 9b3641566..bb9303447 100644 --- a/connector/src/main/java/org/geysermc/connector/network/translators/collision/CollisionManager.java +++ b/connector/src/main/java/org/geysermc/connector/network/translators/collision/CollisionManager.java @@ -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 getCollidableBlocks(BoundingBox box) { - List 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 blocks; + return new BlockPositionIterator(minCollisionX, minCollisionY, minCollisionZ, maxCollisionX, maxCollisionY, maxCollisionZ); } - public List 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 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 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 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; diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/collision/translators/BlockCollision.java b/connector/src/main/java/org/geysermc/connector/network/translators/collision/translators/BlockCollision.java index 0b23973e8..869ff97d3 100644 --- a/connector/src/main/java/org/geysermc/connector/network/translators/collision/translators/BlockCollision.java +++ b/connector/src/main/java/org/geysermc/connector/network/translators/collision/translators/BlockCollision.java @@ -40,16 +40,6 @@ public class BlockCollision { @Getter protected final BoundingBox[] boundingBoxes; - @EqualsAndHashCode.Exclude - protected final ThreadLocal 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 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; } diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/collision/translators/DirtPathCollision.java b/connector/src/main/java/org/geysermc/connector/network/translators/collision/translators/DirtPathCollision.java index aa9a082fa..e4d1b8f5a 100644 --- a/connector/src/main/java/org/geysermc/connector/network/translators/collision/translators/DirtPathCollision.java +++ b/connector/src/main/java/org/geysermc/connector/network/translators/collision/translators/DirtPathCollision.java @@ -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); } diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/collision/translators/DoorCollision.java b/connector/src/main/java/org/geysermc/connector/network/translators/collision/translators/DoorCollision.java index 8fb3e6981..d1b8e45aa 100644 --- a/connector/src/main/java/org/geysermc/connector/network/translators/collision/translators/DoorCollision.java +++ b/connector/src/main/java/org/geysermc/connector/network/translators/collision/translators/DoorCollision.java @@ -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 diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/collision/translators/ScaffoldingCollision.java b/connector/src/main/java/org/geysermc/connector/network/translators/collision/translators/ScaffoldingCollision.java index 06cb0265e..9aed59636 100644 --- a/connector/src/main/java/org/geysermc/connector/network/translators/collision/translators/ScaffoldingCollision.java +++ b/connector/src/main/java/org/geysermc/connector/network/translators/collision/translators/ScaffoldingCollision.java @@ -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); } diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/collision/translators/SnowCollision.java b/connector/src/main/java/org/geysermc/connector/network/translators/collision/translators/SnowCollision.java index 471969d89..35ac13056 100644 --- a/connector/src/main/java/org/geysermc/connector/network/translators/collision/translators/SnowCollision.java +++ b/connector/src/main/java/org/geysermc/connector/network/translators/collision/translators/SnowCollision.java @@ -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); } } diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/collision/translators/TrapdoorCollision.java b/connector/src/main/java/org/geysermc/connector/network/translators/collision/translators/TrapdoorCollision.java index a8a35256f..aef966edf 100644 --- a/connector/src/main/java/org/geysermc/connector/network/translators/collision/translators/TrapdoorCollision.java +++ b/connector/src/main/java/org/geysermc/connector/network/translators/collision/translators/TrapdoorCollision.java @@ -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); diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/world/block/entity/PistonBlockEntity.java b/connector/src/main/java/org/geysermc/connector/network/translators/world/block/entity/PistonBlockEntity.java index c22287a4b..b255fa5d7 100644 --- a/connector/src/main/java/org/geysermc/connector/network/translators/world/block/entity/PistonBlockEntity.java +++ b/connector/src/main/java/org/geysermc/connector/network/translators/world/block/entity/PistonBlockEntity.java @@ -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; } diff --git a/connector/src/main/java/org/geysermc/connector/utils/BlockPositionIterator.java b/connector/src/main/java/org/geysermc/connector/utils/BlockPositionIterator.java new file mode 100644 index 000000000..c7ef28c82 --- /dev/null +++ b/connector/src/main/java/org/geysermc/connector/utils/BlockPositionIterator.java @@ -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; + } +} \ No newline at end of file diff --git a/connector/src/main/java/org/geysermc/connector/utils/BlockUtils.java b/connector/src/main/java/org/geysermc/connector/utils/BlockUtils.java index fd877a522..723d0bd99 100644 --- a/connector/src/main/java/org/geysermc/connector/utils/BlockUtils.java +++ b/connector/src/main/java/org/geysermc/connector/utils/BlockUtils.java @@ -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)); } }