mirror of
https://github.com/GeyserMC/Geyser.git
synced 2025-12-19 14:59:27 +00:00
Happy ghast movement
This commit is contained in:
@@ -66,7 +66,7 @@ public class DisplayBaseEntity extends Entity {
|
||||
this.setRiderSeatPosition(this.baseTranslation);
|
||||
this.moveRelative(this.baseTranslation.getX(), this.baseTranslation.getY(), this.baseTranslation.getZ(), yaw, pitch, headYaw, false);
|
||||
} else {
|
||||
EntityUtils.updateMountOffset(this, this.vehicle, true, true, false);
|
||||
EntityUtils.updateMountOffset(this, this.vehicle, true, true, 0, 1);
|
||||
this.updateBedrockMetadata();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -621,7 +621,7 @@ public class Entity implements GeyserEntity {
|
||||
Entity passenger = passengers.get(i);
|
||||
if (passenger != null) {
|
||||
boolean rider = i == 0;
|
||||
EntityUtils.updateMountOffset(passenger, this, rider, true, passengers.size() > 1);
|
||||
EntityUtils.updateMountOffset(passenger, this, rider, true, i, passengers.size());
|
||||
passenger.updateBedrockMetadata();
|
||||
}
|
||||
}
|
||||
@@ -633,7 +633,7 @@ public class Entity implements GeyserEntity {
|
||||
protected void updateMountOffset() {
|
||||
if (vehicle != null) {
|
||||
boolean rider = vehicle.getPassengers().get(0) == this;
|
||||
EntityUtils.updateMountOffset(this, vehicle, rider, true, vehicle.getPassengers().size() > 1);
|
||||
EntityUtils.updateMountOffset(this, vehicle, rider, true, vehicle.getPassengers().indexOf(this), vehicle.getPassengers().size());
|
||||
updateBedrockMetadata();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -41,7 +41,9 @@ import org.cloudburstmc.protocol.bedrock.packet.MobEquipmentPacket;
|
||||
import org.cloudburstmc.protocol.bedrock.packet.UpdateAttributesPacket;
|
||||
import org.geysermc.geyser.entity.EntityDefinition;
|
||||
import org.geysermc.geyser.entity.attribute.GeyserAttributeType;
|
||||
import org.geysermc.geyser.entity.type.living.animal.HappyGhastEntity;
|
||||
import org.geysermc.geyser.entity.vehicle.ClientVehicle;
|
||||
import org.geysermc.geyser.entity.vehicle.HappyGhastVehicleComponent;
|
||||
import org.geysermc.geyser.inventory.GeyserItemStack;
|
||||
import org.geysermc.geyser.item.Items;
|
||||
import org.geysermc.geyser.registry.type.ItemMapping;
|
||||
@@ -49,6 +51,7 @@ import org.geysermc.geyser.scoreboard.Team;
|
||||
import org.geysermc.geyser.session.GeyserSession;
|
||||
import org.geysermc.geyser.translator.item.ItemTranslator;
|
||||
import org.geysermc.geyser.util.AttributeUtils;
|
||||
import org.geysermc.geyser.util.EntityUtils;
|
||||
import org.geysermc.geyser.util.InteractionResult;
|
||||
import org.geysermc.geyser.util.MathUtils;
|
||||
import org.geysermc.mcprotocollib.protocol.data.game.entity.EquipmentSlot;
|
||||
@@ -510,7 +513,13 @@ public class LivingEntity extends Entity {
|
||||
}
|
||||
}
|
||||
case ATTACK_DAMAGE -> newAttributes.add(calculateAttribute(javaAttribute, GeyserAttributeType.ATTACK_DAMAGE));
|
||||
case FLYING_SPEED -> newAttributes.add(calculateAttribute(javaAttribute, GeyserAttributeType.FLYING_SPEED));
|
||||
case FLYING_SPEED -> {
|
||||
AttributeData attributeData = calculateAttribute(javaAttribute, GeyserAttributeType.FLYING_SPEED);
|
||||
newAttributes.add(attributeData);
|
||||
if (this instanceof HappyGhastEntity ghast && ghast.getVehicleComponent() instanceof HappyGhastVehicleComponent component) {
|
||||
component.setFlyingSpeed(attributeData.getValue());
|
||||
}
|
||||
}
|
||||
case FOLLOW_RANGE -> newAttributes.add(calculateAttribute(javaAttribute, GeyserAttributeType.FOLLOW_RANGE));
|
||||
case KNOCKBACK_RESISTANCE -> newAttributes.add(calculateAttribute(javaAttribute, GeyserAttributeType.KNOCKBACK_RESISTANCE));
|
||||
case JUMP_STRENGTH -> newAttributes.add(calculateAttribute(javaAttribute, GeyserAttributeType.HORSE_JUMP_STRENGTH));
|
||||
@@ -519,9 +528,40 @@ public class LivingEntity extends Entity {
|
||||
setAttributeScale((float) AttributeUtils.calculateValue(javaAttribute));
|
||||
updateBedrockMetadata();
|
||||
}
|
||||
case WATER_MOVEMENT_EFFICIENCY -> {
|
||||
if (this instanceof ClientVehicle clientVehicle) {
|
||||
clientVehicle.getVehicleComponent().setWaterMovementEfficiency(AttributeUtils.calculateValue(javaAttribute));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected boolean hasBodyArmor() {
|
||||
return this.hasValidEquippableItemForSlot(EquipmentSlot.BODY);
|
||||
}
|
||||
|
||||
private boolean hasValidEquippableItemForSlot(EquipmentSlot slot) {
|
||||
// MojMap LivingEntity#hasItemInSlot
|
||||
GeyserItemStack itemInSlot = equipment.get(slot);
|
||||
if (itemInSlot != null) {
|
||||
// MojMap LivingEntity#isEquippableInSlot
|
||||
Equippable equippable = itemInSlot.getComponent(DataComponentTypes.EQUIPPABLE);
|
||||
if (equippable != null) {
|
||||
return slot == equippable.slot() &&
|
||||
canUseSlot(slot) &&
|
||||
EntityUtils.equipmentUsableByEntity(session, equippable, this.definition.entityType());
|
||||
} else {
|
||||
return slot == EquipmentSlot.MAIN_HAND && canUseSlot(EquipmentSlot.MAIN_HAND);
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
protected boolean canUseSlot(EquipmentSlot slot) {
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculates the complete attribute value to send to Bedrock. Will be overriden if attributes need to be cached.
|
||||
|
||||
@@ -27,36 +27,60 @@ package org.geysermc.geyser.entity.type.living.animal;
|
||||
|
||||
import org.checkerframework.checker.nullness.qual.NonNull;
|
||||
import org.checkerframework.checker.nullness.qual.Nullable;
|
||||
import org.cloudburstmc.math.TrigMath;
|
||||
import org.cloudburstmc.math.vector.Vector2f;
|
||||
import org.cloudburstmc.math.vector.Vector3f;
|
||||
import org.cloudburstmc.protocol.bedrock.data.AttributeData;
|
||||
import org.cloudburstmc.protocol.bedrock.data.entity.EntityFlag;
|
||||
import org.geysermc.geyser.entity.EntityDefinition;
|
||||
import org.geysermc.geyser.entity.type.Entity;
|
||||
import org.geysermc.geyser.entity.type.player.SessionPlayerEntity;
|
||||
import org.geysermc.geyser.entity.vehicle.ClientVehicle;
|
||||
import org.geysermc.geyser.entity.vehicle.HappyGhastVehicleComponent;
|
||||
import org.geysermc.geyser.entity.vehicle.VehicleComponent;
|
||||
import org.geysermc.geyser.inventory.GeyserItemStack;
|
||||
import org.geysermc.geyser.item.type.Item;
|
||||
import org.geysermc.geyser.session.GeyserSession;
|
||||
import org.geysermc.geyser.session.cache.tags.ItemTag;
|
||||
import org.geysermc.geyser.session.cache.tags.Tag;
|
||||
import org.geysermc.geyser.util.AttributeUtils;
|
||||
import org.geysermc.geyser.util.InteractionResult;
|
||||
import org.geysermc.geyser.util.InteractiveTag;
|
||||
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.type.BooleanEntityMetadata;
|
||||
import org.geysermc.mcprotocollib.protocol.data.game.entity.player.Hand;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
|
||||
public class HappyGhastEntity extends AnimalEntity {
|
||||
public class HappyGhastEntity extends AnimalEntity implements ClientVehicle {
|
||||
|
||||
public static final float[] X_OFFSETS = {0.0F, -1.7F, 0.0F, 1.7F};
|
||||
public static final float[] Z_OFFSETS = {1.7F, 0.0F, -1.7F, 0.0F};
|
||||
|
||||
private final HappyGhastVehicleComponent vehicleComponent = new HappyGhastVehicleComponent(this, 0.0f);
|
||||
private boolean staysStill;
|
||||
|
||||
public HappyGhastEntity(GeyserSession session, int entityId, long geyserId, UUID uuid, EntityDefinition<?> definition, Vector3f position, Vector3f motion, float yaw, float pitch, float headYaw) {
|
||||
super(session, entityId, geyserId, uuid, definition, position, motion, yaw, pitch, headYaw);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void initializeMetadata() {
|
||||
super.initializeMetadata();
|
||||
// BDS 1.21.90
|
||||
setFlag(EntityFlag.CAN_FLY, true);
|
||||
setFlag(EntityFlag.TAMED, true);
|
||||
setFlag(EntityFlag.CAN_WALK, true);
|
||||
setFlag(EntityFlag.TAMED, true);
|
||||
setFlag(EntityFlag.BODY_ROTATION_ALWAYS_FOLLOWS_HEAD, true);
|
||||
setFlag(EntityFlag.COLLIDABLE, true);
|
||||
|
||||
setFlag(EntityFlag.WASD_AIR_CONTROLLED, true);
|
||||
setFlag(EntityFlag.DOES_SERVER_AUTH_ONLY_DISMOUNT, true);
|
||||
|
||||
// TODO: verify which flags are necessary
|
||||
|
||||
setAirSupply(100);
|
||||
propertyManager.add("minecraft:can_move", true);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -71,6 +95,7 @@ public class HappyGhastEntity extends AnimalEntity {
|
||||
}
|
||||
|
||||
public void setStaysStill(BooleanEntityMetadata entityMetadata) {
|
||||
staysStill = entityMetadata.getPrimitiveValue();
|
||||
propertyManager.add("minecraft:can_move", !entityMetadata.getPrimitiveValue());
|
||||
updateBedrockEntityProperties();
|
||||
}
|
||||
@@ -124,4 +149,65 @@ public class HappyGhastEntity extends AnimalEntity {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public VehicleComponent<?> getVehicleComponent() {
|
||||
return vehicleComponent;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Vector3f getRiddenInput(Vector2f input) {
|
||||
float x = input.getX();
|
||||
float y = 0.0f;
|
||||
float z = 0.0f;
|
||||
|
||||
if (input.getY() != 0.0f) {
|
||||
float pitch = session.getPlayerEntity().getPitch();
|
||||
z = TrigMath.cos(pitch * TrigMath.DEG_TO_RAD);
|
||||
y = -TrigMath.sin(pitch * TrigMath.DEG_TO_RAD);
|
||||
if (input.getY() < 0.0f) {
|
||||
z *= -0.5f;
|
||||
y *= -0.5f;
|
||||
}
|
||||
}
|
||||
|
||||
if (session.getInputCache().wasJumping()) {
|
||||
y += 0.5f;
|
||||
}
|
||||
|
||||
return Vector3f.from(x, y, z).mul(3.9f * vehicleComponent.getFlyingSpeed());
|
||||
}
|
||||
|
||||
@Override
|
||||
public float getVehicleSpeed() {
|
||||
return 1; // TODO this doesnt seem right?
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isClientControlled() {
|
||||
if (!hasBodyArmor() || getFlag(EntityFlag.NO_AI) || staysStill) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return getFirstPassenger() instanceof SessionPlayerEntity;
|
||||
}
|
||||
|
||||
private Entity getFirstPassenger() {
|
||||
return passengers.isEmpty() ? null : passengers.get(0);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void updateAttribute(Attribute javaAttribute, List<AttributeData> newAttributes) {
|
||||
super.updateAttribute(javaAttribute, newAttributes);
|
||||
if (javaAttribute.getType() instanceof AttributeType.Builtin type) {
|
||||
if (type == AttributeType.Builtin.CAMERA_DISTANCE) {
|
||||
vehicleComponent.setCameraDistance((float) AttributeUtils.calculateValue(javaAttribute));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean canUseSlot(EquipmentSlot slot) {
|
||||
return slot != EquipmentSlot.BODY ? super.canUseSlot(slot) : this.isAlive() && !this.isBaby();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -47,6 +47,7 @@ import org.geysermc.geyser.session.cache.tags.Tag;
|
||||
import org.geysermc.geyser.util.EntityUtils;
|
||||
import org.geysermc.geyser.util.InteractionResult;
|
||||
import org.geysermc.geyser.util.InteractiveTag;
|
||||
import org.geysermc.mcprotocollib.protocol.data.game.entity.EquipmentSlot;
|
||||
import org.geysermc.mcprotocollib.protocol.data.game.entity.metadata.type.BooleanEntityMetadata;
|
||||
import org.geysermc.mcprotocollib.protocol.data.game.entity.metadata.type.IntEntityMetadata;
|
||||
import org.geysermc.mcprotocollib.protocol.data.game.entity.player.Hand;
|
||||
@@ -169,8 +170,8 @@ public class StriderEntity extends AnimalEntity implements Tickable, ClientVehic
|
||||
}
|
||||
|
||||
@Override
|
||||
public Vector2f getAdjustedInput(Vector2f input) {
|
||||
return Vector2f.UNIT_Y;
|
||||
public Vector3f getRiddenInput(Vector2f input) {
|
||||
return Vector3f.UNIT_Z;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -195,4 +196,9 @@ public class StriderEntity extends AnimalEntity implements Tickable, ClientVehic
|
||||
public boolean canWalkOnLava() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean canUseSlot(EquipmentSlot slot) {
|
||||
return slot != EquipmentSlot.SADDLE ? super.canUseSlot(slot) : this.isAlive() && !this.isBaby();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -48,6 +48,7 @@ import org.geysermc.geyser.session.cache.tags.Tag;
|
||||
import org.geysermc.geyser.util.EntityUtils;
|
||||
import org.geysermc.geyser.util.InteractionResult;
|
||||
import org.geysermc.geyser.util.InteractiveTag;
|
||||
import org.geysermc.mcprotocollib.protocol.data.game.entity.EquipmentSlot;
|
||||
import org.geysermc.mcprotocollib.protocol.data.game.entity.metadata.type.IntEntityMetadata;
|
||||
import org.geysermc.mcprotocollib.protocol.data.game.entity.player.Hand;
|
||||
|
||||
@@ -128,8 +129,8 @@ public class PigEntity extends TemperatureVariantAnimal implements Tickable, Cli
|
||||
}
|
||||
|
||||
@Override
|
||||
public Vector2f getAdjustedInput(Vector2f input) {
|
||||
return Vector2f.UNIT_Y;
|
||||
public Vector3f getRiddenInput(Vector2f input) {
|
||||
return Vector3f.UNIT_Z;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -154,4 +155,9 @@ public class PigEntity extends TemperatureVariantAnimal implements Tickable, Cli
|
||||
public JavaRegistryKey<BuiltInVariant> variantRegistry() {
|
||||
return JavaRegistries.PIG_VARIANT;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean canUseSlot(EquipmentSlot slot) {
|
||||
return slot != EquipmentSlot.SADDLE ? super.canUseSlot(slot) : this.isAlive() && !this.isBaby();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -45,6 +45,7 @@ import org.geysermc.geyser.session.cache.tags.ItemTag;
|
||||
import org.geysermc.geyser.session.cache.tags.Tag;
|
||||
import org.geysermc.geyser.util.InteractionResult;
|
||||
import org.geysermc.geyser.util.InteractiveTag;
|
||||
import org.geysermc.mcprotocollib.protocol.data.game.entity.EquipmentSlot;
|
||||
import org.geysermc.mcprotocollib.protocol.data.game.entity.metadata.type.ByteEntityMetadata;
|
||||
import org.geysermc.mcprotocollib.protocol.data.game.entity.player.Hand;
|
||||
|
||||
@@ -286,4 +287,13 @@ public class AbstractHorseEntity extends AnimalEntity {
|
||||
return InteractionResult.SUCCESS;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean canUseSlot(EquipmentSlot slot) {
|
||||
if (slot != EquipmentSlot.SADDLE) {
|
||||
return super.canUseSlot(slot);
|
||||
} else {
|
||||
return isAlive() && !isBaby() && getFlag(EntityFlag.TAMED);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -155,8 +155,9 @@ public class CamelEntity extends AbstractHorseEntity implements ClientVehicle {
|
||||
}
|
||||
|
||||
@Override
|
||||
public Vector2f getAdjustedInput(Vector2f input) {
|
||||
return input.mul(0.5f, input.getY() < 0 ? 0.25f : 1.0f);
|
||||
public Vector3f getRiddenInput(Vector2f input) {
|
||||
input = input.mul(0.5f, input.getY() < 0 ? 0.25f : 1.0f);
|
||||
return Vector3f.from(input.getX(), 0.0, input.getY());
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -29,6 +29,7 @@ import org.cloudburstmc.math.vector.Vector3f;
|
||||
import org.cloudburstmc.protocol.bedrock.data.entity.EntityDataTypes;
|
||||
import org.geysermc.geyser.entity.EntityDefinition;
|
||||
import org.geysermc.geyser.session.GeyserSession;
|
||||
import org.geysermc.mcprotocollib.protocol.data.game.entity.EquipmentSlot;
|
||||
import org.geysermc.mcprotocollib.protocol.data.game.entity.metadata.type.IntEntityMetadata;
|
||||
|
||||
import java.util.UUID;
|
||||
@@ -44,4 +45,9 @@ public class HorseEntity extends AbstractHorseEntity {
|
||||
dirtyMetadata.put(EntityDataTypes.VARIANT, value & 255);
|
||||
dirtyMetadata.put(EntityDataTypes.MARK_VARIANT, (value >> 8) % 5);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean canUseSlot(EquipmentSlot slot) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -35,6 +35,7 @@ import org.geysermc.geyser.session.GeyserSession;
|
||||
import org.geysermc.geyser.session.cache.tags.ItemTag;
|
||||
import org.geysermc.geyser.session.cache.tags.Tag;
|
||||
import org.geysermc.geyser.util.MathUtils;
|
||||
import org.geysermc.mcprotocollib.protocol.data.game.entity.EquipmentSlot;
|
||||
import org.geysermc.mcprotocollib.protocol.data.game.entity.metadata.type.IntEntityMetadata;
|
||||
|
||||
import java.util.UUID;
|
||||
@@ -61,4 +62,9 @@ public class LlamaEntity extends ChestedHorseEntity {
|
||||
protected @Nullable Tag<Item> getFoodTag() {
|
||||
return ItemTag.LLAMA_FOOD;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean canUseSlot(EquipmentSlot slot) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -90,13 +90,13 @@ public class CamelVehicleComponent extends VehicleComponent<CamelEntity> {
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Vector3f getInputVelocity(VehicleContext ctx, float speed) {
|
||||
protected Vector3f getInputVector(VehicleContext ctx, float speed, Vector3f input) {
|
||||
if (isStationary()) {
|
||||
return Vector3f.ZERO;
|
||||
}
|
||||
|
||||
SessionPlayerEntity player = vehicle.getSession().getPlayerEntity();
|
||||
Vector3f inputVelocity = super.getInputVelocity(ctx, speed);
|
||||
Vector3f inputVelocity = super.getInputVector(ctx, speed, input);
|
||||
float jumpStrength = player.getVehicleJumpStrength();
|
||||
|
||||
if (jumpStrength > 0) {
|
||||
@@ -117,11 +117,11 @@ public class CamelVehicleComponent extends VehicleComponent<CamelEntity> {
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Vector2f getVehicleRotation() {
|
||||
protected Vector2f getRiddenRotation() {
|
||||
if (isStationary()) {
|
||||
return Vector2f.from(vehicle.getYaw(), vehicle.getPitch());
|
||||
}
|
||||
return super.getVehicleRotation();
|
||||
return super.getRiddenRotation();
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -26,14 +26,18 @@
|
||||
package org.geysermc.geyser.entity.vehicle;
|
||||
|
||||
import org.cloudburstmc.math.vector.Vector2f;
|
||||
import org.cloudburstmc.math.vector.Vector3f;
|
||||
|
||||
public interface ClientVehicle {
|
||||
VehicleComponent<?> getVehicleComponent();
|
||||
|
||||
Vector2f getAdjustedInput(Vector2f input);
|
||||
// MojMap LivingEntity#getRiddenInput
|
||||
Vector3f getRiddenInput(Vector2f input);
|
||||
|
||||
// MojMap LivingEntity#getRiddenSpeed
|
||||
float getVehicleSpeed();
|
||||
|
||||
// MojMap Mob#getControllingPassenger
|
||||
boolean isClientControlled();
|
||||
|
||||
default boolean canWalkOnLava() {
|
||||
|
||||
@@ -0,0 +1,128 @@
|
||||
/*
|
||||
* Copyright (c) 2025 GeyserMC. http://geysermc.org
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*
|
||||
* @author GeyserMC
|
||||
* @link https://github.com/GeyserMC/Geyser
|
||||
*/
|
||||
|
||||
package org.geysermc.geyser.entity.vehicle;
|
||||
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
import org.cloudburstmc.math.vector.Vector3d;
|
||||
import org.cloudburstmc.protocol.bedrock.data.entity.EntityDataTypes;
|
||||
import org.geysermc.erosion.util.BlockPositionIterator;
|
||||
import org.geysermc.geyser.entity.type.living.animal.HappyGhastEntity;
|
||||
import org.geysermc.geyser.entity.type.player.SessionPlayerEntity;
|
||||
import org.geysermc.geyser.level.block.Blocks;
|
||||
import org.geysermc.geyser.level.block.Fluid;
|
||||
import org.geysermc.geyser.level.block.type.BlockState;
|
||||
import org.geysermc.geyser.level.physics.BoundingBox;
|
||||
import org.geysermc.geyser.util.MathUtils;
|
||||
import org.geysermc.mcprotocollib.protocol.data.game.entity.attribute.AttributeType;
|
||||
|
||||
@Setter
|
||||
@Getter
|
||||
public class HappyGhastVehicleComponent extends VehicleComponent<HappyGhastEntity> {
|
||||
|
||||
private float flyingSpeed;
|
||||
private float cameraDistance;
|
||||
|
||||
public HappyGhastVehicleComponent(HappyGhastEntity vehicle, float stepHeight) {
|
||||
super(vehicle, stepHeight);
|
||||
// Happy Ghast has different defaults
|
||||
flyingSpeed = 0.05f;
|
||||
moveSpeed = 0.05f;
|
||||
cameraDistance = 8.0f;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void updateRotation() {
|
||||
float addYaw = MathUtils.wrapDegrees(getRiddenRotation().getX() - vehicle.getYaw()) * 0.08f;
|
||||
vehicle.setYaw(vehicle.getYaw() + addYaw);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onMount() {
|
||||
super.onMount();
|
||||
SessionPlayerEntity playerEntity = vehicle.getSession().getPlayerEntity();
|
||||
playerEntity.getDirtyMetadata().put(EntityDataTypes.SEAT_LOCK_RIDER_ROTATION, false);
|
||||
playerEntity.getDirtyMetadata().put(EntityDataTypes.SEAT_LOCK_RIDER_ROTATION_DEGREES, 181f);
|
||||
playerEntity.getDirtyMetadata().put(EntityDataTypes.SEAT_THIRD_PERSON_CAMERA_RADIUS, cameraDistance);
|
||||
playerEntity.getDirtyMetadata().put(EntityDataTypes.SEAT_CAMERA_RELAX_DISTANCE_SMOOTHING, cameraDistance * 0.75f);
|
||||
playerEntity.getDirtyMetadata().put(EntityDataTypes.CONTROLLING_RIDER_SEAT_INDEX, (byte) 0);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDismount() {
|
||||
super.onDismount();
|
||||
SessionPlayerEntity playerEntity = vehicle.getSession().getPlayerEntity();
|
||||
playerEntity.getDirtyMetadata().put(EntityDataTypes.SEAT_THIRD_PERSON_CAMERA_RADIUS, (float) AttributeType.Builtin.CAMERA_DISTANCE.getDef());
|
||||
playerEntity.getDirtyMetadata().put(EntityDataTypes.SEAT_CAMERA_RELAX_DISTANCE_SMOOTHING, cameraDistance * 0.75f);
|
||||
playerEntity.getDirtyMetadata().put(EntityDataTypes.CONTROLLING_RIDER_SEAT_INDEX, (byte) 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Called every session tick while the player is mounted on the vehicle.
|
||||
*/
|
||||
public void tickVehicle() {
|
||||
if (!vehicle.isClientControlled()) {
|
||||
return;
|
||||
}
|
||||
|
||||
VehicleContext ctx = new VehicleContext();
|
||||
ctx.loadSurroundingBlocks();
|
||||
|
||||
// LivingEntity#travelFlying
|
||||
Fluid fluid = checkForFluid(ctx);
|
||||
float drag = switch (fluid) {
|
||||
case WATER -> 0.8f;
|
||||
case LAVA -> 0.5f;
|
||||
case EMPTY -> 0.91f;
|
||||
};
|
||||
// HappyGhast#travel
|
||||
travel(ctx, flyingSpeed * 5.0f / 3.0f);
|
||||
vehicle.setMotion(vehicle.getMotion().mul(drag));
|
||||
}
|
||||
|
||||
private Fluid checkForFluid(VehicleContext ctx) {
|
||||
Fluid result = Fluid.EMPTY;
|
||||
|
||||
BoundingBox box = boundingBox.clone();
|
||||
box.expand(-0.001);
|
||||
|
||||
Vector3d min = box.getMin();
|
||||
Vector3d max = box.getMax();
|
||||
|
||||
BlockPositionIterator iter = BlockPositionIterator.fromMinMax(min.getFloorX(), min.getFloorY(), min.getFloorZ(), max.getFloorX(), max.getFloorY(), max.getFloorZ());
|
||||
for (iter.reset(); iter.hasNext(); iter.next()) {
|
||||
BlockState blockState = ctx.getBlock(iter);
|
||||
if (blockState.is(Blocks.WATER)) {
|
||||
return Fluid.WATER; // Water takes priority over lava
|
||||
}
|
||||
if (blockState.is(Blocks.LAVA)) {
|
||||
result = Fluid.LAVA;
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
||||
@@ -26,6 +26,8 @@
|
||||
package org.geysermc.geyser.entity.vehicle;
|
||||
|
||||
import it.unimi.dsi.fastutil.objects.ObjectDoublePair;
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
import org.checkerframework.checker.nullness.qual.Nullable;
|
||||
import org.cloudburstmc.math.TrigMath;
|
||||
import org.cloudburstmc.math.vector.Vector2f;
|
||||
@@ -64,20 +66,27 @@ public class VehicleComponent<T extends LivingEntity & ClientVehicle> {
|
||||
private static final float MIN_VELOCITY = 0.003f;
|
||||
|
||||
protected final T vehicle;
|
||||
@Getter
|
||||
protected final BoundingBox boundingBox;
|
||||
protected Vector2f lastSentRotation; // (yaw, pitch)
|
||||
|
||||
protected float stepHeight;
|
||||
@Getter @Setter
|
||||
protected float moveSpeed;
|
||||
protected double gravity;
|
||||
@Getter @Setter
|
||||
protected double waterMovementEfficiency;
|
||||
protected int effectLevitation;
|
||||
protected boolean effectSlowFalling;
|
||||
protected boolean effectWeaving;
|
||||
|
||||
public VehicleComponent(T vehicle, float stepHeight) {
|
||||
this.vehicle = vehicle;
|
||||
this.lastSentRotation = Vector2f.from(vehicle.getYaw(), vehicle.getPitch());
|
||||
this.stepHeight = stepHeight;
|
||||
this.moveSpeed = (float) AttributeType.Builtin.MOVEMENT_SPEED.getDef();
|
||||
this.gravity = AttributeType.Builtin.GRAVITY.getDef();
|
||||
this.waterMovementEfficiency = AttributeType.Builtin.WATER_MOVEMENT_EFFICIENCY.getDef();
|
||||
|
||||
double width = vehicle.getBoundingBoxWidth();
|
||||
double height = vehicle.getBoundingBoxHeight();
|
||||
@@ -117,10 +126,6 @@ public class VehicleComponent<T extends LivingEntity & ClientVehicle> {
|
||||
boundingBox.translate(vec);
|
||||
}
|
||||
|
||||
public BoundingBox getBoundingBox() {
|
||||
return this.boundingBox;
|
||||
}
|
||||
|
||||
public void setEffect(Effect effect, int effectAmplifier) {
|
||||
switch (effect) {
|
||||
case LEVITATION -> effectLevitation = effectAmplifier + 1;
|
||||
@@ -137,14 +142,6 @@ public class VehicleComponent<T extends LivingEntity & ClientVehicle> {
|
||||
}
|
||||
}
|
||||
|
||||
public void setMoveSpeed(float moveSpeed) {
|
||||
this.moveSpeed = moveSpeed;
|
||||
}
|
||||
|
||||
public float getMoveSpeed() {
|
||||
return moveSpeed;
|
||||
}
|
||||
|
||||
public void setStepHeight(float stepHeight) {
|
||||
this.stepHeight = MathUtils.clamp(stepHeight, 1.0f, 10.0f);
|
||||
}
|
||||
@@ -193,6 +190,13 @@ public class VehicleComponent<T extends LivingEntity & ClientVehicle> {
|
||||
}
|
||||
}
|
||||
|
||||
protected void updateRotation() {
|
||||
Vector2f rot = getRiddenRotation();
|
||||
vehicle.setYaw(rot.getX());
|
||||
vehicle.setHeadYaw(rot.getX());
|
||||
vehicle.setPitch(rot.getY());
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds velocity of all colliding fluids to the vehicle, and returns the height of the fluid to use for movement.
|
||||
*
|
||||
@@ -208,6 +212,7 @@ public class VehicleComponent<T extends LivingEntity & ClientVehicle> {
|
||||
|
||||
BlockPositionIterator iter = BlockPositionIterator.fromMinMax(min.getFloorX(), min.getFloorY(), min.getFloorZ(), max.getFloorX(), max.getFloorY(), max.getFloorZ());
|
||||
|
||||
// Mojmap Entity#updateInWaterStateAndDoFluidPushing
|
||||
double waterHeight = getFluidHeightAndApplyMovement(ctx, iter, Fluid.WATER, 0.014, min.getY());
|
||||
double lavaHeight = getFluidHeightAndApplyMovement(ctx, iter, Fluid.LAVA, vehicle.getSession().getDimensionType().ultrawarm() ? 0.007 : 0.007 / 3, min.getY());
|
||||
|
||||
@@ -341,11 +346,12 @@ public class VehicleComponent<T extends LivingEntity & ClientVehicle> {
|
||||
}
|
||||
|
||||
/**
|
||||
* Java edition returns the zero vector if the length of the input vector is less than 0.0001
|
||||
* Java edition returns the zero vector if the length of the input vector is less than 0.00001f
|
||||
*/
|
||||
protected Vector3d javaNormalize(Vector3d vec) {
|
||||
double len = vec.length();
|
||||
return len < 1.0E-4 ? Vector3d.ZERO : Vector3d.from(vec.getX() / len, vec.getY() / len, vec.getZ() / len);
|
||||
// Used to be 1.0E-4
|
||||
return len < 1.0E-5F ? Vector3d.ZERO : Vector3d.from(vec.getX() / len, vec.getY() / len, vec.getZ() / len);
|
||||
}
|
||||
|
||||
protected float getWorldFluidHeight(Fluid fluidType, int blockId) {
|
||||
@@ -373,6 +379,7 @@ public class VehicleComponent<T extends LivingEntity & ClientVehicle> {
|
||||
return BlockUtils.getCollision(adjacentBlockId) instanceof SolidCollision;
|
||||
}
|
||||
|
||||
// Mojmap: LivingEntity#travelInFluid
|
||||
protected void waterMovement(VehicleContext ctx) {
|
||||
double gravity = getGravity();
|
||||
float drag = vehicle.getFlag(EntityFlag.SPRINTING) ? 0.9f : 0.8f; // 0.8f: getBaseMovementSpeedMultiplier
|
||||
@@ -380,6 +387,21 @@ public class VehicleComponent<T extends LivingEntity & ClientVehicle> {
|
||||
boolean falling = vehicle.getMotion().getY() <= 0;
|
||||
|
||||
// NOT IMPLEMENTED: depth strider and dolphins grace
|
||||
// float g = 0.02f;
|
||||
// float waterMovementEfficiencyMultiplier = (float) waterMovementEfficiency;
|
||||
// if (!vehicle.isOnGround()) {
|
||||
// // TODO test
|
||||
// waterMovementEfficiencyMultiplier *= 0.5f;
|
||||
// }
|
||||
//
|
||||
// if (waterMovementEfficiencyMultiplier > 0.0F) {
|
||||
// drag += (0.54600006F - drag) * waterMovementEfficiencyMultiplier;
|
||||
// g += (this.getSpeed() - g) * waterMovementEfficiencyMultiplier;
|
||||
// }
|
||||
|
||||
// if (this.hasEffect(MobEffects.DOLPHINS_GRACE)) {
|
||||
// drag = 0.96F;
|
||||
// }
|
||||
|
||||
boolean horizontalCollision = travel(ctx, 0.02f);
|
||||
|
||||
@@ -570,6 +592,7 @@ public class VehicleComponent<T extends LivingEntity & ClientVehicle> {
|
||||
*
|
||||
* @return true if there was a horizontal collision
|
||||
*/
|
||||
// Mojmap: LivingEntity#moveRelative / LivingEntity#move
|
||||
protected boolean travel(VehicleContext ctx, float speed) {
|
||||
Vector3f motion = vehicle.getMotion();
|
||||
|
||||
@@ -582,9 +605,13 @@ public class VehicleComponent<T extends LivingEntity & ClientVehicle> {
|
||||
Math.abs(motion.getZ()) < MIN_VELOCITY ? 0 : motion.getZ()
|
||||
);
|
||||
|
||||
updateRotation();
|
||||
Vector2f playerInput = vehicle.getSession().getPlayerEntity().getVehicleInput();
|
||||
Vector3f riddenInput = vehicle.getRiddenInput(playerInput.mul(0.98f));
|
||||
|
||||
// !isImmobile
|
||||
if (vehicle.isAlive()) {
|
||||
motion = motion.add(getInputVelocity(ctx, speed));
|
||||
motion = motion.add(getInputVector(ctx, speed, riddenInput));
|
||||
}
|
||||
|
||||
Vector3f movementMultiplier = getBlockMovementMultiplier(ctx);
|
||||
@@ -670,41 +697,30 @@ public class VehicleComponent<T extends LivingEntity & ClientVehicle> {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Translates the player's input into velocity.
|
||||
*
|
||||
* @param ctx context
|
||||
* @param speed multiplier for input
|
||||
* @return velocity
|
||||
*/
|
||||
protected Vector3f getInputVelocity(VehicleContext ctx, float speed) {
|
||||
Vector2f input = vehicle.getSession().getPlayerEntity().getVehicleInput();
|
||||
input = input.mul(0.98f);
|
||||
input = vehicle.getAdjustedInput(input);
|
||||
input = normalizeInput(input);
|
||||
protected Vector3f getInputVector(VehicleContext ctx, float speed, Vector3f input) {
|
||||
double lenSquared = input.lengthSquared();
|
||||
if (lenSquared < 1.0E-7) {
|
||||
return Vector3f.ZERO;
|
||||
}
|
||||
|
||||
if (lenSquared > 1.0f) {
|
||||
input = input.normalize();
|
||||
}
|
||||
input = input.mul(speed);
|
||||
|
||||
// Match player rotation
|
||||
float yaw = vehicle.getSession().getPlayerEntity().getYaw();
|
||||
// Match vehicle rotation
|
||||
float yaw = vehicle.getYaw();
|
||||
float sin = TrigMath.sin(yaw * TrigMath.DEG_TO_RAD);
|
||||
float cos = TrigMath.cos(yaw * TrigMath.DEG_TO_RAD);
|
||||
return Vector3f.from(input.getX() * cos - input.getY() * sin, 0, input.getY() * cos + input.getX() * sin);
|
||||
}
|
||||
|
||||
protected Vector2f normalizeInput(Vector2f input) {
|
||||
float lenSquared = input.lengthSquared();
|
||||
if (lenSquared < 1.0E-7) {
|
||||
return Vector2f.ZERO;
|
||||
} else if (lenSquared > 1.0) {
|
||||
return input.normalize();
|
||||
}
|
||||
return input;
|
||||
return Vector3f.from(input.getX() * cos - input.getZ() * sin, input.getY(), input.getZ() * cos + input.getX() * sin);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the rotation to use for the vehicle. This is based on the player's head rotation.
|
||||
*
|
||||
* @return (yaw, pitch)
|
||||
*/
|
||||
protected Vector2f getVehicleRotation() {
|
||||
protected Vector2f getRiddenRotation() {
|
||||
LivingEntity player = vehicle.getSession().getPlayerEntity();
|
||||
return Vector2f.from(player.getYaw(), player.getPitch() * 0.5f);
|
||||
}
|
||||
@@ -717,7 +733,6 @@ public class VehicleComponent<T extends LivingEntity & ClientVehicle> {
|
||||
*/
|
||||
protected void moveVehicle(Vector3d javaPos) {
|
||||
Vector3f bedrockPos = javaPos.toFloat();
|
||||
Vector2f rotation = getVehicleRotation();
|
||||
|
||||
MoveEntityDeltaPacket moveEntityDeltaPacket = new MoveEntityDeltaPacket();
|
||||
moveEntityDeltaPacket.setRuntimeEntityId(vehicle.getGeyserId());
|
||||
@@ -740,27 +755,25 @@ public class VehicleComponent<T extends LivingEntity & ClientVehicle> {
|
||||
}
|
||||
vehicle.setPosition(bedrockPos);
|
||||
|
||||
if (vehicle.getYaw() != rotation.getX()) {
|
||||
if (vehicle.getYaw() != lastSentRotation.getX()) {
|
||||
moveEntityDeltaPacket.getFlags().add(MoveEntityDeltaPacket.Flag.HAS_YAW);
|
||||
moveEntityDeltaPacket.setYaw(rotation.getX());
|
||||
vehicle.setYaw(rotation.getX());
|
||||
moveEntityDeltaPacket.setYaw(vehicle.getYaw());
|
||||
}
|
||||
if (vehicle.getPitch() != rotation.getY()) {
|
||||
if (vehicle.getPitch() != lastSentRotation.getY()) {
|
||||
moveEntityDeltaPacket.getFlags().add(MoveEntityDeltaPacket.Flag.HAS_PITCH);
|
||||
moveEntityDeltaPacket.setPitch(rotation.getY());
|
||||
vehicle.setPitch(rotation.getY());
|
||||
moveEntityDeltaPacket.setPitch(vehicle.getPitch());
|
||||
}
|
||||
if (vehicle.getHeadYaw() != rotation.getX()) { // Same as yaw
|
||||
if (vehicle.getHeadYaw() != lastSentRotation.getX()) { // Same as yaw
|
||||
moveEntityDeltaPacket.getFlags().add(MoveEntityDeltaPacket.Flag.HAS_HEAD_YAW);
|
||||
moveEntityDeltaPacket.setHeadYaw(rotation.getX());
|
||||
vehicle.setHeadYaw(rotation.getX());
|
||||
moveEntityDeltaPacket.setHeadYaw(vehicle.getYaw());
|
||||
}
|
||||
lastSentRotation = Vector2f.from(vehicle.getYaw(), vehicle.getPitch());
|
||||
|
||||
if (!moveEntityDeltaPacket.getFlags().isEmpty()) {
|
||||
vehicle.getSession().sendUpstreamPacket(moveEntityDeltaPacket);
|
||||
}
|
||||
|
||||
ServerboundMoveVehiclePacket moveVehiclePacket = new ServerboundMoveVehiclePacket(javaPos, rotation.getX(), rotation.getY(), vehicle.isOnGround());
|
||||
ServerboundMoveVehiclePacket moveVehiclePacket = new ServerboundMoveVehiclePacket(javaPos, vehicle.getYaw(), vehicle.getPitch(), vehicle.isOnGround());
|
||||
vehicle.getSession().sendDownstreamPacket(moveVehiclePacket);
|
||||
}
|
||||
|
||||
|
||||
@@ -213,9 +213,6 @@ public class MapHasher<Type> {
|
||||
}
|
||||
|
||||
public HashCode build() {
|
||||
if (unhashed != null) {
|
||||
System.out.println(unhashed);
|
||||
}
|
||||
return encoder.map(map);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -48,9 +48,12 @@ import org.geysermc.geyser.session.GeyserSession;
|
||||
import org.geysermc.geyser.session.dialog.Dialog;
|
||||
import org.geysermc.geyser.util.MinecraftKey;
|
||||
import org.geysermc.mcprotocollib.protocol.data.game.chat.ChatType;
|
||||
import org.geysermc.mcprotocollib.protocol.data.game.entity.type.EntityType;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.Objects;
|
||||
import java.util.Optional;
|
||||
|
||||
@@ -64,6 +67,14 @@ public class JavaRegistries {
|
||||
Block::javaId, Block::javaIdentifier, key -> Optional.ofNullable(BlockRegistries.JAVA_IDENTIFIER_TO_ID.get(key.asString())).orElse(-1));
|
||||
public static final JavaRegistryKey<Item> ITEM = createHardcoded("item", Registries.JAVA_ITEMS,
|
||||
Item::javaId, Item::javaKey, key -> Optional.ofNullable(Registries.JAVA_ITEM_IDENTIFIERS.get(key.asString())).map(Item::javaId).orElse(-1));
|
||||
public static JavaRegistryKey<EntityType> ENTITY_TYPE = createHardcoded("entity_type", Arrays.asList(EntityType.values()), EntityType::ordinal,
|
||||
type -> MinecraftKey.key(type.name().toLowerCase(Locale.ROOT)), key -> {
|
||||
try {
|
||||
return EntityType.valueOf(key.value().toUpperCase(Locale.ROOT)).ordinal();
|
||||
} catch (IllegalArgumentException exception) {
|
||||
return -1; // Non-existent entity type
|
||||
}
|
||||
});
|
||||
|
||||
public static final JavaRegistryKey<ChatType> CHAT_TYPE = create("chat_type");
|
||||
public static final JavaRegistryKey<JavaDimension> DIMENSION_TYPE = create("dimension_type");
|
||||
@@ -95,6 +106,11 @@ public class JavaRegistries {
|
||||
|
||||
private static <T> JavaRegistryKey<T> createHardcoded(String key, ListRegistry<T> registry, RegistryNetworkMapper<T> networkSerializer,
|
||||
RegistryIdentifierMapper<T> identifierMapper, RegistryIdMapper idMapper) {
|
||||
return createHardcoded(key, registry.get(), networkSerializer, identifierMapper, idMapper);
|
||||
}
|
||||
|
||||
private static <T> JavaRegistryKey<T> createHardcoded(String key, List<T> registry, RegistryNetworkMapper<T> networkSerializer,
|
||||
RegistryIdentifierMapper<T> identifierMapper, RegistryIdMapper idMapper) {
|
||||
return create(key, new HardcodedLookup<>(registry, networkSerializer, identifierMapper, idMapper));
|
||||
}
|
||||
|
||||
@@ -130,7 +146,7 @@ public class JavaRegistries {
|
||||
int get(Key key);
|
||||
}
|
||||
|
||||
private record HardcodedLookup<T>(ListRegistry<T> registry, RegistryNetworkMapper<T> networkMapper, RegistryIdentifierMapper<T> identifierMapper,
|
||||
private record HardcodedLookup<T>(List<T> registry, RegistryNetworkMapper<T> networkMapper, RegistryIdentifierMapper<T> identifierMapper,
|
||||
RegistryIdMapper idMapper) implements JavaRegistryKey.RegistryLookup<T> {
|
||||
|
||||
@Override
|
||||
|
||||
@@ -25,6 +25,7 @@
|
||||
|
||||
package org.geysermc.geyser.translator.protocol.java.entity;
|
||||
|
||||
import org.checkerframework.checker.nullness.qual.NonNull;
|
||||
import org.cloudburstmc.protocol.bedrock.data.entity.EntityDataTypes;
|
||||
import org.cloudburstmc.protocol.bedrock.data.entity.EntityLinkData;
|
||||
import org.cloudburstmc.protocol.bedrock.packet.SetEntityLinkPacket;
|
||||
@@ -50,7 +51,9 @@ public class JavaSetPassengersTranslator extends PacketTranslator<ClientboundSet
|
||||
|
||||
// Handle new/existing passengers
|
||||
List<Entity> newPassengers = new ArrayList<>();
|
||||
for (int passengerId : packet.getPassengerIds()) {
|
||||
int @NonNull [] passengerIds = packet.getPassengerIds();
|
||||
for (int i = 0; i < passengerIds.length; i++) {
|
||||
int passengerId = passengerIds[i];
|
||||
Entity passenger = session.getEntityCache().getEntityByJavaId(passengerId);
|
||||
if (passenger == session.getPlayerEntity()) {
|
||||
session.getPlayerEntity().setVehicle(entity);
|
||||
@@ -76,13 +79,15 @@ public class JavaSetPassengersTranslator extends PacketTranslator<ClientboundSet
|
||||
|
||||
passenger.setVehicle(entity);
|
||||
EntityUtils.updateRiderRotationLock(passenger, entity, true);
|
||||
EntityUtils.updateMountOffset(passenger, entity, rider, true, (packet.getPassengerIds().length > 1));
|
||||
EntityUtils.updateMountOffset(passenger, entity, rider, true, i, packet.getPassengerIds().length);
|
||||
// Force an update to the passenger metadata
|
||||
passenger.updateBedrockMetadata();
|
||||
}
|
||||
|
||||
// Handle passengers that were removed
|
||||
for (Entity passenger : entity.getPassengers()) {
|
||||
List<Entity> passengers = entity.getPassengers();
|
||||
for (int i = 0; i < passengers.size(); i++) {
|
||||
Entity passenger = passengers.get(i);
|
||||
if (passenger == null) {
|
||||
continue;
|
||||
}
|
||||
@@ -93,7 +98,7 @@ public class JavaSetPassengersTranslator extends PacketTranslator<ClientboundSet
|
||||
|
||||
passenger.setVehicle(null);
|
||||
EntityUtils.updateRiderRotationLock(passenger, entity, false);
|
||||
EntityUtils.updateMountOffset(passenger, entity, false, false, (packet.getPassengerIds().length > 1));
|
||||
EntityUtils.updateMountOffset(passenger, entity, false, false, i, packet.getPassengerIds().length);
|
||||
// Force an update to the passenger metadata
|
||||
passenger.updateBedrockMetadata();
|
||||
|
||||
|
||||
@@ -39,15 +39,19 @@ import org.geysermc.geyser.entity.type.Entity;
|
||||
import org.geysermc.geyser.entity.type.TextDisplayEntity;
|
||||
import org.geysermc.geyser.entity.type.living.ArmorStandEntity;
|
||||
import org.geysermc.geyser.entity.type.living.animal.AnimalEntity;
|
||||
import org.geysermc.geyser.entity.type.living.animal.HappyGhastEntity;
|
||||
import org.geysermc.geyser.entity.type.living.animal.horse.CamelEntity;
|
||||
import org.geysermc.geyser.inventory.GeyserItemStack;
|
||||
import org.geysermc.geyser.item.Items;
|
||||
import org.geysermc.geyser.session.GeyserSession;
|
||||
import org.geysermc.geyser.session.cache.registry.JavaRegistries;
|
||||
import org.geysermc.geyser.session.cache.tags.GeyserHolderSet;
|
||||
import org.geysermc.geyser.text.MinecraftLocale;
|
||||
import org.geysermc.mcprotocollib.protocol.data.game.entity.Effect;
|
||||
import org.geysermc.mcprotocollib.protocol.data.game.entity.player.GameMode;
|
||||
import org.geysermc.mcprotocollib.protocol.data.game.entity.player.Hand;
|
||||
import org.geysermc.mcprotocollib.protocol.data.game.entity.type.EntityType;
|
||||
import org.geysermc.mcprotocollib.protocol.data.game.item.component.Equippable;
|
||||
|
||||
import java.util.Locale;
|
||||
|
||||
@@ -166,7 +170,7 @@ public final class EntityUtils {
|
||||
/**
|
||||
* Adjust an entity's height if they have mounted/dismounted an entity.
|
||||
*/
|
||||
public static void updateMountOffset(Entity passenger, Entity mount, boolean rider, boolean riding, boolean moreThanOneEntity) {
|
||||
public static void updateMountOffset(Entity passenger, Entity mount, boolean rider, boolean riding, int index, int passengers) {
|
||||
passenger.setFlag(EntityFlag.RIDING, riding);
|
||||
if (riding) {
|
||||
// Without the Y offset, Bedrock players will find themselves in the floor when mounting
|
||||
@@ -179,7 +183,7 @@ public final class EntityUtils {
|
||||
switch (mount.getDefinition().entityType()) {
|
||||
case CAMEL -> {
|
||||
zOffset = 0.5f;
|
||||
if (moreThanOneEntity) {
|
||||
if (passengers > 1) {
|
||||
if (!rider) {
|
||||
zOffset = -0.7f;
|
||||
}
|
||||
@@ -221,12 +225,18 @@ public final class EntityUtils {
|
||||
}
|
||||
}
|
||||
}
|
||||
case HAPPY_GHAST -> {
|
||||
int seatingIndex = Math.min(index, 4);
|
||||
xOffset = HappyGhastEntity.X_OFFSETS[seatingIndex];
|
||||
yOffset = 3.4f;
|
||||
zOffset = HappyGhastEntity.Z_OFFSETS[seatingIndex];
|
||||
}
|
||||
}
|
||||
if (mount instanceof ChestBoatEntity) {
|
||||
xOffset = 0.15F;
|
||||
} else if (mount instanceof BoatEntity) {
|
||||
// Without the X offset, more than one entity on a boat is stacked on top of each other
|
||||
if (moreThanOneEntity) {
|
||||
if (passengers > 1) {
|
||||
xOffset = rider ? 0.2f : -0.6f;
|
||||
if (passenger instanceof AnimalEntity) {
|
||||
xOffset += 0.2f;
|
||||
@@ -342,6 +352,15 @@ public final class EntityUtils {
|
||||
return translatedEntityName("minecraft", typeName, session);
|
||||
}
|
||||
|
||||
public static boolean equipmentUsableByEntity(GeyserSession session, Equippable equippable, EntityType entity) {
|
||||
if (equippable.allowedEntities() == null) {
|
||||
return true;
|
||||
}
|
||||
|
||||
GeyserHolderSet<EntityType> holderSet = GeyserHolderSet.fromHolderSet(JavaRegistries.ENTITY_TYPE, equippable.allowedEntities());
|
||||
return session.getTagCache().is(holderSet, entity);
|
||||
}
|
||||
|
||||
private EntityUtils() {
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user