Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement Bedrock player collision (pushing) #1357

Draft
wants to merge 6 commits into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
69 changes: 65 additions & 4 deletions connector/src/main/java/org/geysermc/connector/entity/Entity.java
Original file line number Diff line number Diff line change
Expand Up @@ -177,13 +177,18 @@ 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) {
moveAbsolute(session, position, Vector3f.from(yaw, pitch, this.rotation.getZ()), isOnGround, teleported);
}

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);
Expand All @@ -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}.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would just import these, it’s much cleaner.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For Javadocs?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, intellij should give you a context action to do if

* @param session GeyserSession.
* @param position The new position of the entity.
* @param yaw The new yaw of the entity.
Expand All @@ -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.
*/
Expand All @@ -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.
Expand All @@ -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.
Expand All @@ -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.
* <br>
* 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.
* <br>
* This should be called any time an entity moves position.
* @param session the session of the Bedrock player
lukeeey marked this conversation as resolved.
Show resolved Hide resolved
*/
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;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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);

Expand All @@ -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
Expand All @@ -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
Expand Down Expand Up @@ -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);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand Down Expand Up @@ -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);
Expand All @@ -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);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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()))
Expand All @@ -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()))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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;
Expand Down