Prevent large ArrayLists from growing on collision checks

See #2540 - not confirmed to be resolved as we weren't able to replicate a cause for this issue but this solved the issue from artifically invoking it.
This commit is contained in:
Camotoy 2021-09-21 21:06:07 -04:00
parent d5c37e927b
commit c28364c5dd
No known key found for this signature in database
GPG key ID: 7EEFB66FE798081F
4 changed files with 19 additions and 12 deletions

View file

@ -929,7 +929,7 @@ public class GeyserSession implements CommandSender {
// Check to see if the player's position needs updating - a position update should be sent once every 3 seconds // Check to see if the player's position needs updating - a position update should be sent once every 3 seconds
if (spawned && (System.currentTimeMillis() - lastMovementTimestamp) > 3000) { if (spawned && (System.currentTimeMillis() - lastMovementTimestamp) > 3000) {
// Recalculate in case something else changed position // Recalculate in case something else changed position
Vector3d position = collisionManager.adjustBedrockPosition(playerEntity.getPosition(), playerEntity.isOnGround()); Vector3d position = collisionManager.adjustBedrockPosition(playerEntity.getPosition(), playerEntity.isOnGround(), false);
// A null return value cancels the packet // A null return value cancels the packet
if (position != null) { if (position != null) {
ClientPlayerPositionPacket packet = new ClientPlayerPositionPacket(playerEntity.isOnGround(), ClientPlayerPositionPacket packet = new ClientPlayerPositionPacket(playerEntity.isOnGround(),

View file

@ -150,7 +150,7 @@ public class PistonCache {
Vector3d delta = totalDisplacement.sub(playerDisplacement); Vector3d delta = totalDisplacement.sub(playerDisplacement);
// Check if the piston is pushing a player into collision // Check if the piston is pushing a player into collision
delta = session.getCollisionManager().correctPlayerMovement(delta, true); delta = session.getCollisionManager().correctPlayerMovement(delta, true, false);
session.getCollisionManager().getPlayerBoundingBox().translate(delta.getX(), delta.getY(), delta.getZ()); session.getCollisionManager().getPlayerBoundingBox().translate(delta.getX(), delta.getY(), delta.getZ());

View file

@ -97,7 +97,7 @@ public class BedrockMovePlayerTranslator extends PacketTranslator<MovePlayerPack
} }
if (isValidMove(session, packet.getMode(), entity.getPosition(), packet.getPosition())) { if (isValidMove(session, packet.getMode(), entity.getPosition(), packet.getPosition())) {
Vector3d position = session.getCollisionManager().adjustBedrockPosition(packet.getPosition(), packet.isOnGround()); Vector3d position = session.getCollisionManager().adjustBedrockPosition(packet.getPosition(), packet.isOnGround(), packet.getMode() == MovePlayerPacket.Mode.TELEPORT);
if (position != null) { // A null return value cancels the packet if (position != null) { // A null return value cancels the packet
Packet movePacket; Packet movePacket;
if (rotationChanged) { if (rotationChanged) {

View file

@ -135,7 +135,7 @@ public class CollisionManager {
* @param onGround whether the Bedrock player is on the ground * @param onGround whether the Bedrock player is on the ground
* @return the position to send to the Java server, or null to cancel sending the packet * @return the position to send to the Java server, or null to cancel sending the packet
*/ */
public Vector3d adjustBedrockPosition(Vector3f bedrockPosition, boolean onGround) { public Vector3d adjustBedrockPosition(Vector3f bedrockPosition, boolean onGround, boolean teleported) {
PistonCache pistonCache = session.getPistonCache(); PistonCache pistonCache = session.getPistonCache();
// Bedrock clients tend to fall off of honey blocks, so we need to teleport them to the new position // Bedrock clients tend to fall off of honey blocks, so we need to teleport them to the new position
if (pistonCache.isPlayerAttachedToHoney()) { if (pistonCache.isPlayerAttachedToHoney()) {
@ -150,7 +150,7 @@ public class CollisionManager {
Vector3d startingPos = playerBoundingBox.getBottomCenter(); Vector3d startingPos = playerBoundingBox.getBottomCenter();
Vector3d movement = position.sub(startingPos); Vector3d movement = position.sub(startingPos);
Vector3d adjustedMovement = correctPlayerMovement(movement, false); Vector3d adjustedMovement = correctPlayerMovement(movement, false, teleported);
playerBoundingBox.translate(adjustedMovement.getX(), adjustedMovement.getY(), adjustedMovement.getZ()); playerBoundingBox.translate(adjustedMovement.getX(), adjustedMovement.getY(), adjustedMovement.getZ());
playerBoundingBox.translate(pistonCache.getPlayerMotion().getX(), pistonCache.getPlayerMotion().getY(), pistonCache.getPlayerMotion().getZ()); playerBoundingBox.translate(pistonCache.getPlayerMotion().getX(), pistonCache.getPlayerMotion().getY(), pistonCache.getPlayerMotion().getZ());
// Correct player position // Correct player position
@ -211,17 +211,22 @@ public class CollisionManager {
// Expand volume by 1 in each direction to include moving blocks // Expand volume by 1 in each direction to include moving blocks
double pistonExpand = session.getPistonCache().getPistons().isEmpty() ? 0 : 1; 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 // Loop through all blocks that could collide
int minCollisionX = (int) Math.floor(position.getX() - ((box.getSizeX() / 2) + COLLISION_TOLERANCE + pistonExpand)); int minCollisionX = (int) Math.floor(position.getX() - ((sizeX / 2) + COLLISION_TOLERANCE + pistonExpand));
int maxCollisionX = (int) Math.floor(position.getX() + (box.getSizeX() / 2) + COLLISION_TOLERANCE + pistonExpand); int maxCollisionX = (int) Math.floor(position.getX() + (sizeX / 2) + COLLISION_TOLERANCE + pistonExpand);
// Y extends 0.5 blocks down because of fence hitboxes // 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 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 minCollisionZ = (int) Math.floor(position.getZ() - ((sizeZ / 2) + COLLISION_TOLERANCE + pistonExpand));
int maxCollisionZ = (int) Math.floor(position.getZ() + (box.getSizeZ() / 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 y = minCollisionY; y < maxCollisionY + 1; y++) {
for (int x = minCollisionX; x < maxCollisionX + 1; x++) { for (int x = minCollisionX; x < maxCollisionX + 1; x++) {
@ -276,8 +281,10 @@ public class CollisionManager {
return true; return true;
} }
public Vector3d correctPlayerMovement(Vector3d movement, boolean checkWorld) { public Vector3d correctPlayerMovement(Vector3d movement, boolean checkWorld, boolean teleported) {
if (!checkWorld && session.getPistonCache().getPistons().isEmpty()) { // There is nothing to check // On the teleported check: see https://github.com/GeyserMC/Geyser/issues/2540
// As of this commit we don't know how it happens but we don't need to check movement here anyway in that case
if (teleported || (!checkWorld && session.getPistonCache().getPistons().isEmpty())) { // There is nothing to check
return movement; return movement;
} }
return correctMovement(movement, playerBoundingBox, session.getPlayerEntity().isOnGround(), PLAYER_STEP_UP, checkWorld); return correctMovement(movement, playerBoundingBox, session.getPlayerEntity().isOnGround(), PLAYER_STEP_UP, checkWorld);