Mirror von
https://github.com/GeyserMC/Geyser.git
synchronisiert 2024-12-26 08:10:11 +01:00
Movement checks (#2547)
This avoids ArrayList allocations and https://github.com/GeyserMC/Geyser/issues/2540.
Dieser Commit ist enthalten in:
Ursprung
6f4d433561
Commit
9a8795988f
@ -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;
|
||||
|
@ -73,7 +73,10 @@ public class BedrockMovePlayerTranslator extends PacketTranslator<MovePlayerPack
|
||||
// Send book update before the player moves
|
||||
session.getBookEditCache().checkForSend();
|
||||
|
||||
session.confirmTeleport(packet.getPosition().toDouble().sub(0, EntityType.PLAYER.getOffset(), 0));
|
||||
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);
|
||||
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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 blocks;
|
||||
return new BlockPositionIterator(minCollisionX, minCollisionY, minCollisionZ, maxCollisionX, maxCollisionY, maxCollisionZ);
|
||||
}
|
||||
|
||||
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;
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
@ -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));
|
||||
}
|
||||
}
|
||||
|
Laden…
In neuem Issue referenzieren
Einen Benutzer sperren