diff --git a/connector/src/main/java/org/geysermc/connector/entity/Entity.java b/connector/src/main/java/org/geysermc/connector/entity/Entity.java
index 5e825e89260..f638b5555a6 100644
--- a/connector/src/main/java/org/geysermc/connector/entity/Entity.java
+++ b/connector/src/main/java/org/geysermc/connector/entity/Entity.java
@@ -177,6 +177,10 @@ public void moveRelative(GeyserSession session, double relX, double relY, double
moveEntityPacket.setTeleported(false);
session.sendUpstreamPacket(moveEntityPacket);
+
+ if (relX != 0 || relY != 0 || relZ != 0) {
+ checkPlayerCollision(session);
+ }
}
public void moveAbsolute(GeyserSession session, Vector3f position, float yaw, float pitch, boolean isOnGround, boolean teleported) {
@@ -184,6 +188,7 @@ public void moveAbsolute(GeyserSession session, Vector3f position, float yaw, fl
}
public void moveAbsolute(GeyserSession session, Vector3f position, Vector3f rotation, boolean isOnGround, boolean teleported) {
+ boolean positionChanged = !position.equals(this.position);
setPosition(position);
setRotation(rotation);
setOnGround(isOnGround);
@@ -196,10 +201,14 @@ public void moveAbsolute(GeyserSession session, Vector3f position, Vector3f rota
moveEntityPacket.setTeleported(teleported);
session.sendUpstreamPacket(moveEntityPacket);
+
+ if (positionChanged) {
+ checkPlayerCollision(session);
+ }
}
/**
- * Teleports an entity to a new location. Used in JavaEntityTeleportTranslator.
+ * Teleports an entity to a new location. Used in {@link org.geysermc.connector.network.translators.java.entity.JavaEntityTeleportTranslator}.
* @param session GeyserSession.
* @param position The new position of the entity.
* @param yaw The new yaw of the entity.
@@ -211,7 +220,7 @@ public void teleport(GeyserSession session, Vector3f position, float yaw, float
}
/**
- * Updates an entity's head position. Used in JavaEntityHeadLookTranslator.
+ * Updates an entity's head position. Used in {@link org.geysermc.connector.network.translators.java.entity.JavaEntityHeadLookTranslator}.
* @param session GeyserSession.
* @param headYaw The new head rotation of the entity.
*/
@@ -220,7 +229,7 @@ public void updateHeadLookRotation(GeyserSession session, float headYaw) {
}
/**
- * Updates an entity's position and rotation. Used in JavaEntityPositionRotationTranslator.
+ * Updates an entity's position and rotation. Used in {@link org.geysermc.connector.network.translators.java.entity.JavaEntityPositionRotationTranslator}.
* @param session GeyserSession
* @param moveX The new X offset of the current position.
* @param moveY The new Y offset of the current position.
@@ -234,7 +243,7 @@ public void updatePositionAndRotation(GeyserSession session, double moveX, doubl
}
/**
- * Updates an entity's rotation. Used in JavaEntityRotationTranslator.
+ * Updates an entity's rotation. Used in {@link org.geysermc.connector.network.translators.java.entity.JavaEntityRotationTranslator}.
* @param session GeyserSession.
* @param yaw The new yaw of the entity.
* @param pitch The new pitch of the entity.
@@ -244,6 +253,58 @@ public void updateRotation(GeyserSession session, float yaw, float pitch, boolea
updatePositionAndRotation(session, 0, 0, 0, yaw, pitch, isOnGround);
}
+ /**
+ * Unlike fishing rod pulling behavior that is calculated serverside on Bedrock and clientside on Java,
+ * Bedrock player "pushing" is nonexistent on Bedrock.
+ *
+ * This code checks to see if a Geyser player should be moved, and if so applies motion to the player
+ * which sends movement packets back.
+ *
+ * This should be called any time an entity moves position.
+ * @param session the session of the Bedrock player
+ */
+ protected void checkPlayerCollision(GeyserSession session) {
+ if (session.getRidingVehicleEntity() == this) return;
+ if (doesYCoordinateIntersect(session)) {
+ double distanceX = session.getPlayerEntity().getPosition().getX() - this.position.getX();
+ double distanceZ = session.getPlayerEntity().getPosition().getZ() - this.position.getZ();
+ double largestDistance = Math.max(Math.abs(distanceX), Math.abs(distanceZ));
+ if (largestDistance <= 0.65 && canCollideWithPlayer(session)) {
+ double largestDistanceSquareRoot = Math.sqrt(largestDistance);
+ double velocityX = (distanceX / 20) / largestDistanceSquareRoot;
+ double velocityZ = (distanceZ / 20) / largestDistanceSquareRoot;
+
+ SetEntityMotionPacket packet = new SetEntityMotionPacket();
+ packet.setRuntimeEntityId(session.getPlayerEntity().getGeyserId());
+ packet.setMotion(Vector3f.from(velocityX, 0, velocityZ));
+ session.sendUpstreamPacket(packet);
+ }
+ }
+ }
+
+ /**
+ * Ensure that collision is possible if this entity is a player. Should be overridden for players as their team
+ * might change the status of this.
+ * @param session the Bedrock client session
+ * @return if this entity can collide with the session's player
+ */
+ protected boolean canCollideWithPlayer(GeyserSession session) {
+ return true;
+ }
+
+ /**
+ * @param session the Bedrock client session
+ * @return true if the Y level of this entity intersects with player entity
+ */
+ protected boolean doesYCoordinateIntersect(GeyserSession session) {
+ float playerYMin = session.getPlayerEntity().getPosition().getY() - session.getPlayerEntity().getEntityType().getOffset();
+ float playerYMax = playerYMin + session.getPlayerEntity().getEntityType().getHeight();
+ float entityYMax = this.position.getY() + this.entityType.getHeight();
+ float entityYMin = this.position.getY();
+ if (entityYMin > playerYMax) return false; // Entity is higher than the player
+ return !(playerYMin > entityYMax); // Player is higher than the entity
+ }
+
public void updateBedrockAttributes(GeyserSession session) {
if (!valid) return;
diff --git a/connector/src/main/java/org/geysermc/connector/entity/PlayerEntity.java b/connector/src/main/java/org/geysermc/connector/entity/PlayerEntity.java
index 390110d1849..ec6d31de939 100644
--- a/connector/src/main/java/org/geysermc/connector/entity/PlayerEntity.java
+++ b/connector/src/main/java/org/geysermc/connector/entity/PlayerEntity.java
@@ -130,6 +130,7 @@ public void sendPlayer(GeyserSession session) {
@Override
public void moveAbsolute(GeyserSession session, Vector3f position, Vector3f rotation, boolean isOnGround, boolean teleported) {
+ boolean positionChanged = !position.add(0, entityType.getOffset(), 0).equals(this.position);
setPosition(position);
setRotation(rotation);
@@ -153,6 +154,10 @@ public void moveAbsolute(GeyserSession session, Vector3f position, Vector3f rota
if (rightParrot != null) {
rightParrot.moveAbsolute(session, position, rotation, true, teleported);
}
+
+ if (session.getPlayerEntity() != this && positionChanged) {
+ checkPlayerCollision(session);
+ }
}
@Override
@@ -175,6 +180,10 @@ public void moveRelative(GeyserSession session, double relX, double relY, double
if (rightParrot != null) {
rightParrot.moveRelative(session, relX, relY, relZ, rotation, true);
}
+
+ if (session.getPlayerEntity() != this && (relX != 0 || relY != 0 || relZ != 0)) {
+ checkPlayerCollision(session);
+ }
}
@Override
@@ -218,6 +227,32 @@ public void updateRotation(GeyserSession session, float yaw, float pitch, boolea
}
}
+ @Override
+ protected boolean canCollideWithPlayer(GeyserSession session) {
+ Team team = session.getWorldCache().getScoreboard().getTeamFor(username);
+ if (team == null) return true;
+ switch (team.getCollisionRule()) {
+ case NEVER:
+ return false;
+ case PUSH_OWN_TEAM:
+ return team.getEntities().contains(session.getPlayerEntity().getUsername());
+ case PUSH_OTHER_TEAMS:
+ return !team.getEntities().contains(session.getPlayerEntity().getUsername());
+ default:
+ return true;
+ }
+ }
+
+ @Override
+ protected boolean doesYCoordinateIntersect(GeyserSession session) {
+ float playerYMin = session.getPlayerEntity().getPosition().getY() - session.getPlayerEntity().getEntityType().getOffset();
+ float playerYMax = playerYMin + session.getPlayerEntity().getEntityType().getHeight();
+ float thisYMin = this.position.getY() - this.entityType.getOffset();
+ float thisYMax = thisYMin + this.entityType.getHeight();
+ if (thisYMin > playerYMax) return false; // Entity is higher than the player
+ return !(playerYMin > thisYMax); // Player is higher than the entity
+ }
+
@Override
public void setPosition(Vector3f position) {
this.position = position.add(0, entityType.getOffset(), 0);
diff --git a/connector/src/main/java/org/geysermc/connector/entity/living/merchant/VillagerEntity.java b/connector/src/main/java/org/geysermc/connector/entity/living/merchant/VillagerEntity.java
index 7ada302c2d2..a9b79112545 100644
--- a/connector/src/main/java/org/geysermc/connector/entity/living/merchant/VillagerEntity.java
+++ b/connector/src/main/java/org/geysermc/connector/entity/living/merchant/VillagerEntity.java
@@ -26,19 +26,18 @@
package org.geysermc.connector.entity.living.merchant;
import com.github.steveice10.mc.protocol.data.game.entity.metadata.EntityMetadata;
-import com.github.steveice10.mc.protocol.data.game.entity.metadata.VillagerData;
import com.github.steveice10.mc.protocol.data.game.entity.metadata.Position;
+import com.github.steveice10.mc.protocol.data.game.entity.metadata.VillagerData;
import com.nukkitx.math.vector.Vector3f;
import com.nukkitx.protocol.bedrock.data.entity.EntityData;
import com.nukkitx.protocol.bedrock.data.entity.EntityFlag;
-import com.nukkitx.protocol.bedrock.data.entity.EntityFlags;
-import com.nukkitx.protocol.bedrock.packet.*;
+import com.nukkitx.protocol.bedrock.packet.MoveEntityAbsolutePacket;
import it.unimi.dsi.fastutil.ints.Int2IntMap;
import it.unimi.dsi.fastutil.ints.Int2IntOpenHashMap;
import org.geysermc.connector.entity.type.EntityType;
import org.geysermc.connector.network.session.GeyserSession;
-import org.geysermc.connector.network.translators.world.WorldManager;
import org.geysermc.connector.network.translators.world.block.BlockTranslator;
+
import java.util.regex.Matcher;
import java.util.regex.Pattern;
@@ -95,12 +94,17 @@ public void updateBedrockMetadata(EntityMetadata entityMetadata, GeyserSession s
@Override
public void moveRelative(GeyserSession session, double relX, double relY, double relZ, Vector3f rotation, boolean isOnGround) {
+ if (!metadata.getFlags().getFlag(EntityFlag.SLEEPING)) {
+ // No need to worry about extra processing to compensate for sleeping
+ super.moveRelative(session, relX, relY, relZ, rotation, isOnGround);
+ return;
+ }
int z = 0;
int bedId = 0;
float bedPositionSubtractorW = 0;
float bedPositionSubtractorN = 0;
if (session.getConnector().getConfig().isCacheChunks()) {
- Position bedLocation = new Position((int) position.getFloorX(), (int) position.getFloorY(), (int) position.getFloorZ());
+ Position bedLocation = new Position(position.getFloorX(), position.getFloorY(), position.getFloorZ());
bedId = session.getConnector().getWorldManager().getBlockAt(session, bedLocation);
}
String bedRotationZ = BlockTranslator.getJavaIdBlockMap().inverse().get(bedId);
@@ -110,42 +114,41 @@ public void moveRelative(GeyserSession session, double relX, double relY, double
MoveEntityAbsolutePacket moveEntityPacket = new MoveEntityAbsolutePacket();
moveEntityPacket.setRuntimeEntityId(geyserId);
- //Sets Villager position and rotation when sleeping
- if (!metadata.getFlags().getFlag(EntityFlag.SLEEPING)) {
- moveEntityPacket.setPosition(position);
- moveEntityPacket.setRotation(getBedrockRotation());
- } else {
- //String Setup
- Pattern r = Pattern.compile("facing=([a-z]+)");
- Matcher m = r.matcher(bedRotationZ);
- if (m.find()) {
- switch (m.group(0)){
- case "facing=south":
- //bed is facing south
- z = 180;
- bedPositionSubtractorW = -.5f;
- break;
- case "facing=east":
- //bed is facing east
- z = 90;
- bedPositionSubtractorW = -.5f;
- break;
- case "facing=west":
- //bed is facing west
- z = 270;
- bedPositionSubtractorW = .5f;
- break;
- case "facing=north":
- //rotation does not change because north is 0
- bedPositionSubtractorN = .5f;
- break;
- }
+ // Sets Villager position and rotation when sleeping
+ // String Setup
+ Pattern r = Pattern.compile("facing=([a-z]+)");
+ Matcher m = r.matcher(bedRotationZ);
+ if (m.find()) {
+ switch (m.group(0)) {
+ case "facing=south":
+ //bed is facing south
+ z = 180;
+ bedPositionSubtractorW = -.5f;
+ break;
+ case "facing=east":
+ //bed is facing east
+ z = 90;
+ bedPositionSubtractorW = -.5f;
+ break;
+ case "facing=west":
+ //bed is facing west
+ z = 270;
+ bedPositionSubtractorW = .5f;
+ break;
+ case "facing=north":
+ //rotation does not change because north is 0
+ bedPositionSubtractorN = .5f;
+ break;
}
- moveEntityPacket.setRotation(Vector3f.from(0, 0, z));
- moveEntityPacket.setPosition(Vector3f.from(position.getX() + bedPositionSubtractorW, position.getY(), position.getZ() + bedPositionSubtractorN));
}
+ moveEntityPacket.setRotation(Vector3f.from(0, 0, z));
+ moveEntityPacket.setPosition(Vector3f.from(position.getX() + bedPositionSubtractorW, position.getY(), position.getZ() + bedPositionSubtractorN));
moveEntityPacket.setOnGround(isOnGround);
moveEntityPacket.setTeleported(false);
session.sendUpstreamPacket(moveEntityPacket);
+
+ if (relX != 0 || relY != 0 || relZ != 0) {
+ checkPlayerCollision(session);
+ }
}
}
diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/java/scoreboard/JavaTeamTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/java/scoreboard/JavaTeamTranslator.java
index 998e5effe9c..5b0d3fcc7ee 100644
--- a/connector/src/main/java/org/geysermc/connector/network/translators/java/scoreboard/JavaTeamTranslator.java
+++ b/connector/src/main/java/org/geysermc/connector/network/translators/java/scoreboard/JavaTeamTranslator.java
@@ -60,6 +60,7 @@ public void translate(ServerTeamPacket packet, GeyserSession session) {
case CREATE:
scoreboard.registerNewTeam(packet.getTeamName(), toPlayerSet(packet.getPlayers()))
.setName(MessageUtils.getBedrockMessage(packet.getDisplayName()))
+ .setCollisionRule(packet.getCollisionRule())
.setColor(packet.getColor())
.setNameTagVisibility(packet.getNameTagVisibility())
.setPrefix(MessageUtils.getTranslatedBedrockMessage(packet.getPrefix(), session.getClientData().getLanguageCode()))
@@ -75,6 +76,7 @@ public void translate(ServerTeamPacket packet, GeyserSession session) {
}
team.setName(MessageUtils.getBedrockMessage(packet.getDisplayName()))
+ .setCollisionRule(packet.getCollisionRule())
.setColor(packet.getColor())
.setNameTagVisibility(packet.getNameTagVisibility())
.setPrefix(MessageUtils.getTranslatedBedrockMessage(packet.getPrefix(), session.getClientData().getLanguageCode()))
diff --git a/connector/src/main/java/org/geysermc/connector/scoreboard/Team.java b/connector/src/main/java/org/geysermc/connector/scoreboard/Team.java
index a073e2e99c7..e529877a562 100644
--- a/connector/src/main/java/org/geysermc/connector/scoreboard/Team.java
+++ b/connector/src/main/java/org/geysermc/connector/scoreboard/Team.java
@@ -25,6 +25,7 @@
package org.geysermc.connector.scoreboard;
+import com.github.steveice10.mc.protocol.data.game.scoreboard.CollisionRule;
import com.github.steveice10.mc.protocol.data.game.scoreboard.NameTagVisibility;
import com.github.steveice10.mc.protocol.data.game.scoreboard.TeamColor;
import it.unimi.dsi.fastutil.objects.ObjectOpenHashSet;
@@ -45,6 +46,7 @@ public class Team {
private UpdateType updateType = UpdateType.ADD;
private String name;
+ private CollisionRule collisionRule;
private NameTagVisibility nameTagVisibility;
private String prefix;
private TeamColor color;