Skip to content
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
Original file line number Diff line number Diff line change
Expand Up @@ -136,6 +136,11 @@ public void updateRotation(float yaw, float pitch, boolean isOnGround) {
moveRelative(0, 0, 0, yaw + 90, 0, 0, isOnGround);
}

@Override
public Vector3f position() {
return this.position.down(definition.offset());
}

public void setPaddlingLeft(BooleanEntityMetadata entityMetadata) {
isPaddlingLeft = entityMetadata.getPrimitiveValue();
if (!isPaddlingLeft) {
Expand Down
18 changes: 18 additions & 0 deletions core/src/main/java/org/geysermc/geyser/entity/type/Entity.java
Original file line number Diff line number Diff line change
Expand Up @@ -412,6 +412,24 @@ public void setFlags(ByteEntityMetadata entityMetadata) {
setInvisible((xd & 0x20) == 0x20);
}

/**
* Gets the position of the entity without it being offset.
*
* @return the position of the entity without the offset value
*/
public Vector3f position() {
return this.position;
}

/**
* Tests whether an entity can be pushed by a player. For player entities, the team they're in determines if they can be pushed.
* @param session the Bedrock client session
* @return if this entity can collide with the session's player
*/
public boolean isPushable(GeyserSession session) {
return false;
}

/**
* Set a boolean - whether the entity is invisible or visible
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -124,6 +124,12 @@ protected void moveAbsoluteImmediate(Vector3f position, float yaw, float pitch,
.thenApply(BlockStateValues::getWaterLevel);
}

@Override
public Vector3f position() {
float offset = definition.offset();
return this.position.down(waterLevel.join() == 0 ? -offset : offset);
}

@Override
protected float getGravity() {
if (getFlag(EntityFlag.HAS_GRAVITY) && !isOnGround() && !isInWater()) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -46,9 +46,14 @@
import org.geysermc.geyser.entity.vehicle.HappyGhastVehicleComponent;
import org.geysermc.geyser.inventory.GeyserItemStack;
import org.geysermc.geyser.item.Items;
import org.geysermc.geyser.level.block.Blocks;
import org.geysermc.geyser.level.block.property.Properties;
import org.geysermc.geyser.level.block.type.BlockState;
import org.geysermc.geyser.level.block.type.TrapDoorBlock;
import org.geysermc.geyser.registry.type.ItemMapping;
import org.geysermc.geyser.scoreboard.Team;
import org.geysermc.geyser.session.GeyserSession;
import org.geysermc.geyser.session.cache.tags.BlockTag;
import org.geysermc.geyser.translator.item.ItemTranslator;
import org.geysermc.geyser.util.AttributeUtils;
import org.geysermc.geyser.util.EntityUtils;
Expand Down Expand Up @@ -278,6 +283,29 @@ public void setParticles(ObjectEntityMetadata<List<Particle>> entityMetadata) {
}
}

public boolean isOnClimbableBlock() {
Vector3i blockPos = this.position().toInt();
BlockState state = session.getGeyser().getWorldManager().blockAt(session, blockPos);
if (session.getTagCache().is(BlockTag.CLIMBABLE, state.block())) {
return true;
}

if (state.block() instanceof TrapDoorBlock) {
if (!state.getValue(Properties.OPEN)) {
return false;
} else {
BlockState belowState = session.getGeyser().getWorldManager().blockAt(session, blockPos.down());
return belowState.is(Blocks.LADDER) && belowState.getValue(Properties.HORIZONTAL_FACING) == state.getValue(Properties.HORIZONTAL_FACING);
}
}
return false;
}

@Override
public boolean isPushable(GeyserSession session) {
return this.getFlag(EntityFlag.HAS_GRAVITY) && this.isAlive() && !this.isOnClimbableBlock();
}

protected boolean hasShield(boolean offhand) {
ItemMapping shieldMapping = session.getItemMappings().getStoredItems().shield();
if (offhand) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -210,6 +210,11 @@ public Vector3f getBedrockRotation() {
return Vector3f.from(0, getYaw(), 0);
}

@Override
public Vector3f position() {
return this.position.down(definition.offset());
}

@Override
protected InteractiveTag testInteraction(Hand hand) {
if (definition == EntityDefinitions.CHEST_MINECART || definition == EntityDefinitions.HOPPER_MINECART) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,11 @@ public void moveAbsolute(Vector3f position, float yaw, float pitch, float headYa
super.moveAbsolute(position.add(Vector3f.from(0, definition.offset(), 0)), yaw, pitch, headYaw, isOnGround, teleported);
}

@Override
public Vector3f position() {
return this.position.down(definition.offset());
}

public void setFuseLength(IntEntityMetadata entityMetadata) {
currentTick = entityMetadata.getPrimitiveValue();
setFlag(EntityFlag.IGNITED, true);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,11 @@ public void moveAbsolute(Vector3f position, float yaw, float pitch, float headYa
super.moveAbsolute(position.add(Vector3f.from(0, definition.offset(), 0)), yaw, pitch, headYaw, isOnGround, teleported);
}

@Override
public Vector3f position() {
return this.position.down(definition.offset());
}

@Override
protected void initializeMetadata() {
super.initializeMetadata();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,11 @@ public void despawnEntity() {
super.despawnEntity();
}

@Override
public boolean isPushable(GeyserSession session) {
return false;
}

@Override
public void moveRelative(double relX, double relY, double relZ, float yaw, float pitch, float headYaw, boolean isOnGround) {
moveAbsolute(position.add(relX, relY, relZ), yaw, pitch, headYaw, onGround, false);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -296,4 +296,9 @@ protected boolean canUseSlot(EquipmentSlot slot) {
return isAlive() && !isBaby() && getFlag(EntityFlag.TAMED);
}
}

@Override
public boolean isPushable(GeyserSession session) {
return this.getPassengers().isEmpty();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,11 @@ public void setAngerLevel(IntEntityMetadata entityMetadata) {
dirtyMetadata.put(EntityDataTypes.HEARTBEAT_INTERVAL_TICKS, heartBeatDelay);
}

@Override
public boolean isPushable(GeyserSession session) {
return super.isPushable(session) && !getFlag(EntityFlag.DIGGING) && !getFlag(EntityFlag.EMERGING);
}

@Override
public void tick() {
if (++tickCount % heartBeatDelay == 0 && !silent) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@
import org.geysermc.geyser.entity.type.LivingEntity;
import org.geysermc.geyser.entity.type.living.animal.tameable.ParrotEntity;
import org.geysermc.geyser.level.block.Blocks;
import org.geysermc.geyser.scoreboard.Team;
import org.geysermc.geyser.session.GeyserSession;
import org.geysermc.geyser.util.ChunkUtils;
import org.geysermc.mcprotocollib.protocol.data.game.entity.metadata.EntityMetadata;
Expand Down Expand Up @@ -295,6 +296,23 @@ public void setPosition(Vector3f position) {
return bedPosition;
}

@Override
public boolean isPushable(GeyserSession session) {
boolean canCollide = super.isPushable(session);
if (this instanceof SessionPlayerEntity) { // This is the session player, don't check for team collision rule.
return canCollide;
}

Team team = session.getWorldCache().getScoreboard().getTeamFor(username);
if (team == null) return true;
return switch (team.collisionRule()) {
case NEVER -> false;
case PUSH_OWN_TEAM -> canCollide && team.hasEntity(session.getPlayerEntity().getUsername());
case PUSH_OTHER_TEAMS -> canCollide && !team.hasEntity(session.getPlayerEntity().getUsername());
default -> canCollide;
};
}

public void setAbsorptionHearts(FloatEntityMetadata entityMetadata) {
// Extra hearts - is not metadata but an attribute on Bedrock
UpdateAttributesPacket attributesPacket = new UpdateAttributesPacket();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -506,25 +506,12 @@ public float getJumpVelocity() {
return velocity + 0.1F * session.getEffectCache().getJumpPower();
}

@Override
public boolean isOnClimbableBlock() {
if (session.getGameMode() == GameMode.SPECTATOR) {
return false;
}
Vector3i pos = getPosition().down(EntityDefinitions.PLAYER.offset()).toInt();
BlockState state = session.getGeyser().getWorldManager().blockAt(session, pos);
if (session.getTagCache().is(BlockTag.CLIMBABLE, state.block())) {
return true;
}

if (state.block() instanceof TrapDoorBlock) {
if (!state.getValue(Properties.OPEN)) {
return false;
} else {
BlockState belowState = session.getGeyser().getWorldManager().blockAt(session, pos.down());
return belowState.is(Blocks.LADDER) && belowState.getValue(Properties.HORIZONTAL_FACING) == state.getValue(Properties.HORIZONTAL_FACING);
}
}
return false;
return super.isOnClimbableBlock();
}

public boolean canStartGliding() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@
import org.geysermc.geyser.scoreboard.display.slot.PlayerlistDisplaySlot;
import org.geysermc.geyser.scoreboard.display.slot.SidebarDisplaySlot;
import org.geysermc.geyser.session.GeyserSession;
import org.geysermc.mcprotocollib.protocol.data.game.scoreboard.CollisionRule;
import org.geysermc.mcprotocollib.protocol.data.game.scoreboard.NameTagVisibility;
import org.geysermc.mcprotocollib.protocol.data.game.scoreboard.ScoreboardPosition;
import org.geysermc.mcprotocollib.protocol.data.game.scoreboard.TeamColor;
Expand Down Expand Up @@ -173,7 +174,8 @@ public void registerNewTeam(
Component prefix,
Component suffix,
NameTagVisibility visibility,
TeamColor color
TeamColor color,
CollisionRule collisionRule
) {
Team team = teams.get(teamName);
if (team != null) {
Expand All @@ -183,7 +185,7 @@ public void registerNewTeam(
return;
}

team = new Team(this, teamName, players, name, prefix, suffix, visibility, color);
team = new Team(this, teamName, players, name, prefix, suffix, visibility, color, collisionRule);
teams.put(teamName, team);

// Update command parameters - is safe to send even if the command enum doesn't exist on the client (as of 1.19.51)
Expand Down
11 changes: 10 additions & 1 deletion core/src/main/java/org/geysermc/geyser/scoreboard/Team.java
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@
import org.geysermc.geyser.session.GeyserSession;
import org.geysermc.geyser.text.ChatColor;
import org.geysermc.geyser.translator.text.MessageTranslator;
import org.geysermc.mcprotocollib.protocol.data.game.scoreboard.CollisionRule;
import org.geysermc.mcprotocollib.protocol.data.game.scoreboard.NameTagVisibility;
import org.geysermc.mcprotocollib.protocol.data.game.scoreboard.TeamColor;

Expand All @@ -48,6 +49,7 @@ public final class Team {
private final Set<Entity> managedEntities;
@NonNull private NameTagVisibility nameTagVisibility = NameTagVisibility.ALWAYS;
private TeamColor color;
private CollisionRule collisionRule;

private String name;
private String prefix;
Expand All @@ -62,14 +64,17 @@ public Team(
Component prefix,
Component suffix,
NameTagVisibility visibility,
TeamColor color
TeamColor color,
CollisionRule collisionRule
) {
this.scoreboard = scoreboard;
this.id = id;
this.entities = new ObjectOpenHashSet<>();
this.managedEntities = new ObjectOpenHashSet<>();
this.lastUpdate = LAST_UPDATE_DEFAULT;

this.collisionRule = collisionRule;

// doesn't call entity update
updateProperties(name, prefix, suffix, visibility, color);
// calls entity update
Expand Down Expand Up @@ -316,6 +321,10 @@ public TeamColor color() {
return color;
}

public CollisionRule collisionRule() {
return collisionRule;
}

public long lastUpdate() {
return lastUpdate;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,12 +25,16 @@

package org.geysermc.geyser.translator.protocol.bedrock.entity.player.input;

import org.cloudburstmc.math.GenericMath;
import org.cloudburstmc.math.vector.Vector3d;
import org.cloudburstmc.math.vector.Vector3f;
import org.cloudburstmc.protocol.bedrock.data.PlayerAuthInputData;
import org.cloudburstmc.protocol.bedrock.packet.PlayerAuthInputPacket;
import org.cloudburstmc.protocol.bedrock.packet.SetEntityMotionPacket;
import org.geysermc.geyser.entity.EntityDefinitions;
import org.geysermc.geyser.entity.type.Entity;
import org.geysermc.geyser.entity.type.player.SessionPlayerEntity;
import org.geysermc.geyser.level.physics.BoundingBox;
import org.geysermc.geyser.level.physics.CollisionResult;
import org.geysermc.geyser.session.GeyserSession;
import org.geysermc.geyser.text.ChatColor;
Expand Down Expand Up @@ -216,6 +220,54 @@ static void translate(GeyserSession session, PlayerAuthInputPacket packet) {
if (entity.getRightParrot() != null) {
entity.getRightParrot().moveAbsolute(entity.getPosition(), entity.getYaw(), entity.getPitch(), entity.getHeadYaw(), true, false);
}

// If the player is riding a vehicle or if the player itself cannot be pushed, then don't push.
if (entity.getVehicle() == null && entity.isPushable(session)) {
boolean affectedByPushMotion = false;
for (Entity other : session.getEntityCache().getEntities().values()) {
if (other == entity) {
continue;
}

if (!other.isPushable(session)) {
continue;
}

final BoundingBox entityBoundingBox = new BoundingBox(0, 0, 0, other.getBoundingBoxWidth(), other.getBoundingBoxHeight(), other.getBoundingBoxWidth());
entityBoundingBox.translate(other.position().toDouble());

// If this entity can't collide with the player or isn't near the player or is the player itself, continue.
if (!entityBoundingBox.checkIntersection(session.getCollisionManager().getPlayerBoundingBox())) {
continue;
}

// 1.21.7 entity pushing logic.
float xDistance = entity.getPosition().getX() - other.getPosition().getX();
float zDistance = entity.getPosition().getZ() - other.getPosition().getZ();
float largestDistance = Math.max(Math.abs(xDistance), Math.abs(zDistance));

Vector3f pushVelocity = Vector3f.from(xDistance, 0, zDistance);
if (largestDistance >= 0.01F) {
largestDistance = (float) GenericMath.sqrt(largestDistance);
pushVelocity = pushVelocity.div(largestDistance);
pushVelocity = pushVelocity.mul(Math.min(1 / largestDistance, 1));
pushVelocity = pushVelocity.mul(0.05F);

// The push motion should be relative to the current motion, we don't want player to fly by sending y vel 0.
entity.setMotion(entity.getMotion().add(pushVelocity));
affectedByPushMotion = true;
}
}

if (affectedByPushMotion) {
SetEntityMotionPacket motionPacket = new SetEntityMotionPacket();
motionPacket.setRuntimeEntityId(entity.getGeyserId());
motionPacket.setMotion(entity.getMotion());
// This is really important, or else player going to have weird motion without this tick id.
motionPacket.setTick(packet.getTick());
session.sendUpstreamPacket(motionPacket);
}
}
}

private static boolean isInvalidNumber(float val) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,8 @@ public void translate(GeyserSession session, ClientboundSetPlayerTeamPacket pack
packet.getPrefix(),
packet.getSuffix(),
packet.getNameTagVisibility(),
packet.getColor()
packet.getColor(),
packet.getCollisionRule()
);
} else {
Team team = scoreboard.getTeam(packet.getTeamName());
Expand Down
Loading