Skip to content
Merged
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
16 changes: 12 additions & 4 deletions core/src/main/java/org/geysermc/geyser/entity/type/Entity.java
Original file line number Diff line number Diff line change
Expand Up @@ -171,7 +171,7 @@ protected void initializeMetadata() {
dirtyMetadata.put(EntityDataTypes.SCALE, 1f);
dirtyMetadata.put(EntityDataTypes.COLOR, (byte) 0);
dirtyMetadata.put(EntityDataTypes.AIR_SUPPLY_MAX, getMaxAir());
setDimensions(Pose.STANDING);
setDimensionsFromPose(Pose.STANDING);
setFlag(EntityFlag.HAS_GRAVITY, true);
setFlag(EntityFlag.HAS_COLLISION, true);
setFlag(EntityFlag.CAN_SHOW_NAME, true);
Expand Down Expand Up @@ -405,7 +405,7 @@ public void setFlags(ByteEntityMetadata entityMetadata) {
setFlag(EntityFlag.SPRINTING, (xd & 0x08) == 0x08);

// Swimming is ignored here and instead we rely on the pose
setFlag(EntityFlag.GLIDING, (xd & 0x80) == 0x80);
setGliding((xd & 0x80) == 0x80);

setInvisible((xd & 0x20) == 0x20);
}
Expand All @@ -419,6 +419,13 @@ protected void setInvisible(boolean value) {
setFlag(EntityFlag.INVISIBLE, value);
}

/**
* Set a boolean - whether the entity is gliding
*/
protected void setGliding(boolean value) {
setFlag(EntityFlag.GLIDING, value);
}

/**
* Set an int from 0 - this entity's maximum air - (air / maxAir) represents the percentage of bubbles left
*/
Expand Down Expand Up @@ -529,15 +536,16 @@ public void setGravity(BooleanEntityMetadata entityMetadata) {
*/
public void setPose(Pose pose) {
setFlag(EntityFlag.SLEEPING, pose.equals(Pose.SLEEPING));
// FALL_FLYING is instead set via setFlags
// Triggered when crawling
setFlag(EntityFlag.SWIMMING, pose.equals(Pose.SWIMMING));
setDimensions(pose);
setDimensionsFromPose(pose);
}

/**
* Set the height and width of the entity's bounding box
*/
protected void setDimensions(Pose pose) {
protected void setDimensionsFromPose(Pose pose) {
// No flexibility options for basic entities
setBoundingBoxHeight(definition.height());
setBoundingBoxWidth(definition.width());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,9 +27,7 @@

import org.cloudburstmc.math.vector.Vector3f;
import org.cloudburstmc.protocol.bedrock.data.entity.EntityDataTypes;
import org.cloudburstmc.protocol.bedrock.packet.SetEntityMotionPacket;
import org.geysermc.geyser.entity.EntityDefinition;
import org.geysermc.geyser.entity.type.player.PlayerEntity;
import org.geysermc.geyser.item.Items;
import org.geysermc.geyser.item.TooltipOptions;
import org.geysermc.geyser.session.GeyserSession;
Expand Down Expand Up @@ -72,20 +70,22 @@ public void setPlayerGliding(EntityMetadata<OptionalInt, ?> entityMetadata) {
// and checks to make sure the player that is gliding is the one getting sent the packet
// or else every player near the gliding player will boost too.
if (optional.isPresent() && optional.getAsInt() == session.getPlayerEntity().getEntityId()) {
PlayerEntity entity = session.getPlayerEntity();
float yaw = entity.getYaw();
float pitch = entity.getPitch();
// Uses math from NukkitX
entity.setMotion(Vector3f.from(
-Math.sin(Math.toRadians(yaw)) * Math.cos(Math.toRadians(pitch)) * 2,
-Math.sin(Math.toRadians(pitch)) * 2,
Math.cos(Math.toRadians(yaw)) * Math.cos(Math.toRadians(pitch)) * 2));
// Need to update the EntityMotionPacket or else the player won't boost
SetEntityMotionPacket entityMotionPacket = new SetEntityMotionPacket();
entityMotionPacket.setRuntimeEntityId(entity.getGeyserId());
entityMotionPacket.setMotion(entity.getMotion());

session.sendUpstreamPacket(entityMotionPacket);
// TODO Firework rocket boosting is client side. Sending this boost is no longer needed
// Good luck to whoever is going to try implementing cancelling firework rocket boosting :)
// PlayerEntity entity = session.getPlayerEntity();
// float yaw = entity.getYaw();
// float pitch = entity.getPitch();
// // Uses math from NukkitX
// entity.setMotion(Vector3f.from(
// -Math.sin(Math.toRadians(yaw)) * Math.cos(Math.toRadians(pitch)) * 2,
// -Math.sin(Math.toRadians(pitch)) * 2,
// Math.cos(Math.toRadians(yaw)) * Math.cos(Math.toRadians(pitch)) * 2));
// // Need to update the EntityMotionPacket or else the player won't boost
// SetEntityMotionPacket entityMotionPacket = new SetEntityMotionPacket();
// entityMotionPacket.setRuntimeEntityId(entity.getGeyserId());
// entityMotionPacket.setMotion(entity.getMotion());
//
// session.sendUpstreamPacket(entityMotionPacket);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -206,12 +206,16 @@ public void setLivingEntityFlags(ByteEntityMetadata entityMetadata) {
setFlag(EntityFlag.BLOCKING, isUsingItem && isUsingShield);

// Riptide spin attack
setFlag(EntityFlag.DAMAGE_NEARBY_MOBS, (xd & 0x04) == 0x04);
setSpinAttack((xd & 0x04) == 0x04);

// OptionalPack usage
setFlag(EntityFlag.EMERGING, isUsingItem && isUsingOffhand);
}

protected void setSpinAttack(boolean value) {
setFlag(EntityFlag.DAMAGE_NEARBY_MOBS, value);
}

public void setHealth(FloatEntityMetadata entityMetadata) {
this.health = entityMetadata.getPrimitiveValue();

Expand Down Expand Up @@ -279,12 +283,12 @@ protected boolean isShaking() {
}

@Override
protected void setDimensions(Pose pose) {
protected void setDimensionsFromPose(Pose pose) {
if (pose == Pose.SLEEPING) {
setBoundingBoxWidth(0.2f);
setBoundingBoxHeight(0.2f);
} else {
super.setDimensions(pose);
super.setDimensionsFromPose(pose);
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -63,12 +63,12 @@ public void setScreamer(BooleanEntityMetadata entityMetadata) {
}

@Override
protected void setDimensions(Pose pose) {
protected void setDimensionsFromPose(Pose pose) {
if (pose == Pose.LONG_JUMPING) {
setBoundingBoxWidth(LONG_JUMPING_WIDTH);
setBoundingBoxHeight(LONG_JUMPING_HEIGHT);
} else {
super.setDimensions(pose);
super.setDimensionsFromPose(pose);
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -64,12 +64,12 @@ public void setPose(Pose pose) {
}

@Override
protected void setDimensions(Pose pose) {
protected void setDimensionsFromPose(Pose pose) {
if (getFlag(EntityFlag.DIGGING)) {
setBoundingBoxHeight(DIGGING_HEIGHT);
setBoundingBoxWidth(definition.width());
} else {
super.setDimensions(pose);
super.setDimensionsFromPose(pose);
}
}

Expand All @@ -90,7 +90,7 @@ public void setSnifferState(ObjectEntityMetadata<SnifferState> entityMetadata) {
setFlag(EntityFlag.DIGGING, snifferState == SnifferState.DIGGING);
setFlag(EntityFlag.RISING, snifferState == SnifferState.RISING);

setDimensions(pose);
setDimensionsFromPose(pose);

if (getFlag(EntityFlag.DIGGING)) {
digTicks = DIG_END;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -113,12 +113,12 @@ public void setPose(Pose pose) {
}

@Override
protected void setDimensions(Pose pose) {
protected void setDimensionsFromPose(Pose pose) {
if (pose == Pose.SITTING) {
setBoundingBoxHeight(definition.height() - SITTING_HEIGHT_DIFFERENCE);
setBoundingBoxWidth(definition.width());
} else {
super.setDimensions(pose);
super.setDimensionsFromPose(pose);
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -416,7 +416,7 @@ protected void scoreVisibility(boolean show) {
}

@Override
protected void setDimensions(Pose pose) {
public void setDimensionsFromPose(Pose pose) {
float height;
float width;
switch (pose) {
Expand All @@ -433,7 +433,7 @@ protected void setDimensions(Pose pose) {
width = 0.2f;
}
default -> {
super.setDimensions(pose);
super.setDimensionsFromPose(pose);
return;
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,27 +31,37 @@
import org.checkerframework.checker.nullness.qual.Nullable;
import org.cloudburstmc.math.vector.Vector2f;
import org.cloudburstmc.math.vector.Vector3f;
import org.cloudburstmc.math.vector.Vector3i;
import org.cloudburstmc.protocol.bedrock.data.AttributeData;
import org.cloudburstmc.protocol.bedrock.data.entity.EntityDataTypes;
import org.cloudburstmc.protocol.bedrock.data.entity.EntityFlag;
import org.cloudburstmc.protocol.bedrock.packet.MovePlayerPacket;
import org.cloudburstmc.protocol.bedrock.packet.UpdateAttributesPacket;
import org.geysermc.geyser.entity.EntityDefinitions;
import org.geysermc.geyser.entity.attribute.GeyserAttributeType;
import org.geysermc.geyser.inventory.GeyserItemStack;
import org.geysermc.geyser.item.Items;
import org.geysermc.geyser.level.BedrockDimension;
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.session.GeyserSession;
import org.geysermc.geyser.session.cache.tags.BlockTag;
import org.geysermc.geyser.util.AttributeUtils;
import org.geysermc.geyser.util.DimensionUtils;
import org.geysermc.geyser.util.MathUtils;
import org.geysermc.mcprotocollib.protocol.data.game.entity.Effect;
import org.geysermc.mcprotocollib.protocol.data.game.entity.EquipmentSlot;
import org.geysermc.mcprotocollib.protocol.data.game.entity.attribute.Attribute;
import org.geysermc.mcprotocollib.protocol.data.game.entity.attribute.AttributeType;
import org.geysermc.mcprotocollib.protocol.data.game.entity.metadata.GlobalPos;
import org.geysermc.mcprotocollib.protocol.data.game.entity.metadata.Pose;
import org.geysermc.mcprotocollib.protocol.data.game.entity.metadata.type.ByteEntityMetadata;
import org.geysermc.mcprotocollib.protocol.data.game.entity.metadata.type.FloatEntityMetadata;
import org.geysermc.mcprotocollib.protocol.data.game.entity.player.GameMode;
import org.geysermc.mcprotocollib.protocol.data.game.item.component.DataComponentTypes;
import org.geysermc.mcprotocollib.protocol.data.game.item.component.Equippable;

import java.util.Collections;
import java.util.List;
Expand Down Expand Up @@ -196,6 +206,16 @@ public void setFlags(ByteEntityMetadata entityMetadata) {
}
}

@Override
protected void setGliding(boolean value) {
session.setGliding(value);
}

@Override
protected void setSpinAttack(boolean value) {
session.setSpinAttack(value);
}

/**
* Since 1.19.40, the client must be re-informed of its bounding box on respawn
* See <a href="https://github.com/GeyserMC/Geyser/issues/3370">issue 3370</a>
Expand Down Expand Up @@ -428,4 +448,67 @@ public float getJumpVelocity() {

return velocity + 0.1F * session.getEffectCache().getJumpPower();
}

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;
}

public boolean canStartGliding() {
// You can't start gliding when levitation is applied
if (session.getEffectCache().getEntityEffects().contains(Effect.LEVITATION)) {
return false;
}

if (this.isOnClimbableBlock() || session.getPlayerEntity().isOnGround()) {
return false;
}

if (session.getCollisionManager().isPlayerTouchingWater()) {
return false;
}

// Unfortunately gliding is still client-side, so we cannot force the client to glide even
// if we wanted to. However, we still need to check that gliding is possible even with, say,
// an elytra that does not have the glider component.
for (Map.Entry<EquipmentSlot, GeyserItemStack> entry : session.getPlayerInventory().getEquipment().entrySet()) {
if (entry.getValue().getComponent(DataComponentTypes.GLIDER) != null) {
Equippable equippable = entry.getValue().getComponent(DataComponentTypes.EQUIPPABLE);
if (equippable != null && equippable.slot() == entry.getKey() && !entry.getValue().nextDamageWillBreak()) {
return true;
}
}

// Bedrock will NOT allow flight when not wearing an elytra; even if it doesn't have a glider component
if (entry.getKey() == EquipmentSlot.CHESTPLATE && !entry.getValue().asItem().equals(Items.ELYTRA)) {
return false;
}
}

return false;
}

public void forceFlagUpdate() {
setFlagsDirty(true);
}

public boolean isGliding() {
return getFlag(EntityFlag.GLIDING);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -126,14 +126,14 @@ public int getAmount() {
}

/**
* @return the {@link DataComponents} that aren't the base/default components.
* @return the {@link DataComponents} patch that's sent over the network.
*/
public @Nullable DataComponents getComponents() {
return isEmpty() ? null : components;
}

/**
* @return whether this GeyserItemStack has any additional components on top of
* @return whether this GeyserItemStack has any component modifications additional to
* the base item components.
*/
public boolean hasNonBaseComponents() {
Expand All @@ -159,16 +159,13 @@ public DataComponents getOrCreateComponents() {
*/
@Nullable
public <T> T getComponent(@NonNull DataComponentType<T> type) {
if (components == null) {
return asItem().getComponent(type);
}

T value = components.get(type);
if (value == null) {
return asItem().getComponent(type);
// A data component patch may contain null values to remove base components
// e.g. an elytra without the glider component
if (components != null && components.contains(type)) {
return components.get(type);
}

return value;
return asItem().getComponent(type);
}

public <T> T getComponentElseGet(@NonNull DataComponentType<T> type, Supplier<T> supplier) {
Expand Down Expand Up @@ -251,6 +248,24 @@ public SlotDisplay asSlotDisplay() {
return new ItemStackSlotDisplay(this.getItemStack());
}

public int getMaxDamage() {
return getComponentElseGet(DataComponentTypes.MAX_DAMAGE, () -> 0);
}

public int getDamage() {
// Damage can't be negative
int damage = Math.max(this.getComponentElseGet(DataComponentTypes.DAMAGE, () -> 0), 0);
return Math.min(damage, this.getMaxDamage());
}

public boolean nextDamageWillBreak() {
return this.isDamageable() && this.getDamage() >= this.getMaxDamage() - 1;
}

public boolean isDamageable() {
return getComponent(DataComponentTypes.MAX_DAMAGE) != null && getComponent(DataComponentTypes.UNBREAKABLE) == null && getComponent(DataComponentTypes.DAMAGE) != null;
}

public Item asItem() {
if (isEmpty()) {
return Items.AIR;
Expand Down
Loading