Mirror von
https://github.com/GeyserMC/Geyser.git
synchronisiert 2024-11-03 14:50:19 +01:00
Attribute rewrite
This commit solves a couple issues: - Entities that don't implement LivingEntity don't need to bother with attributes - We don't need to cache all attributes for every entity - Mounted entities' hearts were inconsistent No entity as of this commit caches their attributes except for the player entity.
Dieser Commit ist enthalten in:
Ursprung
ad9987517d
Commit
32bbd8ae1c
@ -29,29 +29,23 @@ import com.github.steveice10.mc.protocol.data.game.entity.metadata.EntityMetadat
|
||||
import com.github.steveice10.mc.protocol.data.game.entity.metadata.MetadataType;
|
||||
import com.github.steveice10.mc.protocol.data.game.entity.metadata.Pose;
|
||||
import com.nukkitx.math.vector.Vector3f;
|
||||
import com.nukkitx.protocol.bedrock.data.AttributeData;
|
||||
import com.nukkitx.protocol.bedrock.data.entity.EntityData;
|
||||
import com.nukkitx.protocol.bedrock.data.entity.EntityDataMap;
|
||||
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.AddEntityPacket;
|
||||
import com.nukkitx.protocol.bedrock.packet.MoveEntityAbsolutePacket;
|
||||
import com.nukkitx.protocol.bedrock.packet.RemoveEntityPacket;
|
||||
import com.nukkitx.protocol.bedrock.packet.SetEntityDataPacket;
|
||||
import it.unimi.dsi.fastutil.longs.LongOpenHashSet;
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
import net.kyori.adventure.text.Component;
|
||||
import org.geysermc.connector.entity.attribute.Attribute;
|
||||
import org.geysermc.connector.entity.attribute.AttributeType;
|
||||
import org.geysermc.connector.entity.living.ArmorStandEntity;
|
||||
import org.geysermc.connector.entity.player.PlayerEntity;
|
||||
import org.geysermc.connector.entity.type.EntityType;
|
||||
import org.geysermc.connector.network.session.GeyserSession;
|
||||
import org.geysermc.connector.network.translators.chat.MessageTranslator;
|
||||
import org.geysermc.connector.utils.AttributeUtils;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
@Getter
|
||||
@Setter
|
||||
@ -77,7 +71,6 @@ public class Entity {
|
||||
protected boolean valid;
|
||||
|
||||
protected LongOpenHashSet passengers = new LongOpenHashSet();
|
||||
protected Map<AttributeType, Attribute> attributes = new HashMap<>();
|
||||
protected EntityDataMap metadata = new EntityDataMap();
|
||||
|
||||
public Entity(long entityId, long geyserId, EntityType entityType, Vector3f position, Vector3f motion, Vector3f rotation) {
|
||||
@ -116,6 +109,7 @@ public class Entity {
|
||||
addEntityPacket.setRotation(getBedrockRotation());
|
||||
addEntityPacket.setEntityType(entityType.getType());
|
||||
addEntityPacket.getMetadata().putAll(metadata);
|
||||
addAdditionalSpawnData(addEntityPacket);
|
||||
|
||||
valid = true;
|
||||
session.sendUpstreamPacket(addEntityPacket);
|
||||
@ -123,6 +117,13 @@ public class Entity {
|
||||
session.getConnector().getLogger().debug("Spawned entity " + entityType + " at location " + position + " with id " + geyserId + " (java id " + entityId + ")");
|
||||
}
|
||||
|
||||
/**
|
||||
* To be overridden in other entity classes, if additional things need to be done to the spawn entity packet.
|
||||
*/
|
||||
public void addAdditionalSpawnData(AddEntityPacket addEntityPacket) {
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Despawns the entity
|
||||
*
|
||||
@ -231,23 +232,6 @@ public class Entity {
|
||||
updatePositionAndRotation(session, 0, 0, 0, yaw, pitch, isOnGround);
|
||||
}
|
||||
|
||||
public void updateBedrockAttributes(GeyserSession session) {
|
||||
if (!valid) return;
|
||||
|
||||
List<AttributeData> attributes = new ArrayList<>();
|
||||
for (Map.Entry<AttributeType, Attribute> entry : this.attributes.entrySet()) {
|
||||
if (!entry.getValue().getType().isBedrockAttribute())
|
||||
continue;
|
||||
|
||||
attributes.add(AttributeUtils.getBedrockAttribute(entry.getValue()));
|
||||
}
|
||||
|
||||
UpdateAttributesPacket updateAttributesPacket = new UpdateAttributesPacket();
|
||||
updateAttributesPacket.setRuntimeEntityId(geyserId);
|
||||
updateAttributesPacket.setAttributes(attributes);
|
||||
session.sendUpstreamPacket(updateAttributesPacket);
|
||||
}
|
||||
|
||||
/**
|
||||
* Applies the Java metadata to the local Bedrock metadata copy
|
||||
* @param entityMetadata the Java entity metadata
|
||||
|
@ -25,6 +25,7 @@
|
||||
|
||||
package org.geysermc.connector.entity;
|
||||
|
||||
import com.github.steveice10.mc.protocol.data.game.entity.attribute.Attribute;
|
||||
import com.github.steveice10.mc.protocol.data.game.entity.metadata.EntityMetadata;
|
||||
import com.github.steveice10.mc.protocol.data.game.entity.metadata.Pose;
|
||||
import com.github.steveice10.mc.protocol.data.game.entity.metadata.Position;
|
||||
@ -38,9 +39,10 @@ import com.nukkitx.protocol.bedrock.data.inventory.ItemData;
|
||||
import com.nukkitx.protocol.bedrock.packet.MobArmorEquipmentPacket;
|
||||
import com.nukkitx.protocol.bedrock.packet.MobEquipmentPacket;
|
||||
import com.nukkitx.protocol.bedrock.packet.UpdateAttributesPacket;
|
||||
import lombok.AccessLevel;
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
import org.geysermc.connector.entity.attribute.AttributeType;
|
||||
import org.geysermc.connector.entity.attribute.GeyserAttributeType;
|
||||
import org.geysermc.connector.entity.type.EntityType;
|
||||
import org.geysermc.connector.network.session.GeyserSession;
|
||||
import org.geysermc.connector.network.translators.item.ItemRegistry;
|
||||
@ -48,8 +50,8 @@ import org.geysermc.connector.utils.AttributeUtils;
|
||||
import org.geysermc.connector.utils.ChunkUtils;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
@Getter
|
||||
@Setter
|
||||
@ -62,6 +64,11 @@ public class LivingEntity extends Entity {
|
||||
protected ItemData hand = ItemData.AIR;
|
||||
protected ItemData offHand = ItemData.AIR;
|
||||
|
||||
@Getter(value = AccessLevel.NONE)
|
||||
protected float health = 1f; // The default value in Java Edition before any entity metadata is sent
|
||||
@Getter(value = AccessLevel.NONE)
|
||||
protected float maxHealth = 20f; // The value Java Edition defaults to if no attribute is given
|
||||
|
||||
/**
|
||||
* A convenience variable for if the entity has reached the maximum frozen ticks and should be shaking
|
||||
*/
|
||||
@ -69,6 +76,9 @@ public class LivingEntity extends Entity {
|
||||
|
||||
public LivingEntity(long entityId, long geyserId, EntityType entityType, Vector3f position, Vector3f motion, Vector3f rotation) {
|
||||
super(entityId, geyserId, entityType, position, motion, rotation);
|
||||
|
||||
// Matches Bedrock behavior; is always set to this
|
||||
metadata.put(EntityData.HEALTH, 1);
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -88,7 +98,13 @@ public class LivingEntity extends Entity {
|
||||
metadata.getFlags().setFlag(EntityFlag.DAMAGE_NEARBY_MOBS, (xd & 0x04) == 0x04);
|
||||
break;
|
||||
case 9:
|
||||
metadata.put(EntityData.HEALTH, entityMetadata.getValue());
|
||||
this.health = (float) entityMetadata.getValue();
|
||||
|
||||
AttributeData healthData = createHealthAttribute();
|
||||
UpdateAttributesPacket attributesPacket = new UpdateAttributesPacket();
|
||||
attributesPacket.setRuntimeEntityId(geyserId);
|
||||
attributesPacket.setAttributes(Collections.singletonList(healthData));
|
||||
session.sendUpstreamPacket(attributesPacket);
|
||||
break;
|
||||
case 10:
|
||||
metadata.put(EntityData.EFFECT_COLOR, entityMetadata.getValue());
|
||||
@ -139,12 +155,12 @@ public class LivingEntity extends Entity {
|
||||
metadata.getFlags().setFlag(EntityFlag.SHAKING, isShaking(session));
|
||||
}
|
||||
|
||||
public void updateAllEquipment(GeyserSession session) {
|
||||
if (!valid) return;
|
||||
|
||||
updateArmor(session);
|
||||
updateMainHand(session);
|
||||
updateOffHand(session);
|
||||
/**
|
||||
* @return a Bedrock health attribute constructed from the data sent from the server
|
||||
*/
|
||||
protected AttributeData createHealthAttribute() {
|
||||
// Default health needs to be specified as the max health in order for maximum hearts to show correctly on mounted entities
|
||||
return new AttributeData(GeyserAttributeType.HEALTH.getBedrockIdentifier(), 0f, this.maxHealth, this.health, this.maxHealth);
|
||||
}
|
||||
|
||||
public void updateArmor(GeyserSession session) {
|
||||
@ -198,37 +214,67 @@ public class LivingEntity extends Entity {
|
||||
session.sendUpstreamPacket(offHandPacket);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateBedrockAttributes(GeyserSession session) {
|
||||
/**
|
||||
* Attributes are properties of an entity that are generally more runtime-based instead of permanent properties.
|
||||
* Movement speed, current attack damage with a weapon, current knockback resistance.
|
||||
*
|
||||
* @param attributes the Java list of attributes sent from the server
|
||||
*/
|
||||
public void updateBedrockAttributes(GeyserSession session, List<Attribute> attributes) {
|
||||
if (!valid) return;
|
||||
|
||||
float maxHealth = this.attributes.containsKey(AttributeType.MAX_HEALTH) ? this.attributes.get(AttributeType.MAX_HEALTH).getValue() : getDefaultMaxHealth();
|
||||
List<AttributeData> newAttributes = new ArrayList<>();
|
||||
|
||||
List<AttributeData> attributes = new ArrayList<>();
|
||||
for (Map.Entry<AttributeType, org.geysermc.connector.entity.attribute.Attribute> entry : this.attributes.entrySet()) {
|
||||
if (!entry.getValue().getType().isBedrockAttribute())
|
||||
continue;
|
||||
if (entry.getValue().getType() == AttributeType.HEALTH) {
|
||||
// Add health attribute to properly show hearts when mounting
|
||||
// TODO: Not a perfect system, since it led to respawn bugs
|
||||
attributes.add(new AttributeData("minecraft:health", 0.0f, maxHealth, metadata.getFloat(EntityData.HEALTH, 20f), maxHealth));
|
||||
continue;
|
||||
for (Attribute attribute : attributes) {
|
||||
// Convert the attribute to a Bedrock version, if relevant
|
||||
updateAttribute(attribute, newAttributes);
|
||||
}
|
||||
|
||||
attributes.add(AttributeUtils.getBedrockAttribute(entry.getValue()));
|
||||
if (newAttributes.isEmpty()) {
|
||||
// If there are Java-only attributes or only attributes that are not translated by us
|
||||
return;
|
||||
}
|
||||
|
||||
UpdateAttributesPacket updateAttributesPacket = new UpdateAttributesPacket();
|
||||
updateAttributesPacket.setRuntimeEntityId(geyserId);
|
||||
updateAttributesPacket.setAttributes(attributes);
|
||||
updateAttributesPacket.setAttributes(newAttributes);
|
||||
session.sendUpstreamPacket(updateAttributesPacket);
|
||||
}
|
||||
|
||||
/**
|
||||
* Used for the health visual when mounting an entity.
|
||||
* @return the default maximum health for the entity.
|
||||
* Takes the Java attribute and adds it to newAttributes as a Bedrock-formatted attribute
|
||||
*/
|
||||
protected float getDefaultMaxHealth() {
|
||||
return 20f;
|
||||
protected void updateAttribute(Attribute javaAttribute, List<AttributeData> newAttributes) {
|
||||
switch (javaAttribute.getType()) {
|
||||
case GENERIC_MAX_HEALTH:
|
||||
this.maxHealth = (float) AttributeUtils.calculateValue(javaAttribute);
|
||||
newAttributes.add(createHealthAttribute());
|
||||
break;
|
||||
case GENERIC_ATTACK_DAMAGE:
|
||||
newAttributes.add(calculateAttribute(javaAttribute, GeyserAttributeType.ATTACK_DAMAGE));
|
||||
break;
|
||||
case GENERIC_FLYING_SPEED:
|
||||
newAttributes.add(calculateAttribute(javaAttribute, GeyserAttributeType.FLYING_SPEED));
|
||||
break;
|
||||
case GENERIC_MOVEMENT_SPEED:
|
||||
newAttributes.add(calculateAttribute(javaAttribute, GeyserAttributeType.MOVEMENT_SPEED));
|
||||
break;
|
||||
case GENERIC_FOLLOW_RANGE:
|
||||
newAttributes.add(calculateAttribute(javaAttribute, GeyserAttributeType.FOLLOW_RANGE));
|
||||
break;
|
||||
case GENERIC_KNOCKBACK_RESISTANCE:
|
||||
newAttributes.add(calculateAttribute(javaAttribute, GeyserAttributeType.KNOCKBACK_RESISTANCE));
|
||||
break;
|
||||
case HORSE_JUMP_STRENGTH:
|
||||
newAttributes.add(calculateAttribute(javaAttribute, GeyserAttributeType.HORSE_JUMP_STRENGTH));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculates the complete attribute value to send to Bedrock. Will be overriden if attributes need to be cached.
|
||||
*/
|
||||
protected AttributeData calculateAttribute(Attribute javaAttribute, GeyserAttributeType type) {
|
||||
return type.getAttribute((float) AttributeUtils.calculateValue(javaAttribute));
|
||||
}
|
||||
}
|
||||
|
@ -1,44 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2019-2021 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.connector.entity.attribute;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
import lombok.ToString;
|
||||
|
||||
@Getter
|
||||
@Setter
|
||||
@AllArgsConstructor
|
||||
@ToString
|
||||
public class Attribute {
|
||||
|
||||
private AttributeType type;
|
||||
private float minimum;
|
||||
private float maximum;
|
||||
private float value;
|
||||
private float defaultValue;
|
||||
}
|
@ -25,12 +25,13 @@
|
||||
|
||||
package org.geysermc.connector.entity.attribute;
|
||||
|
||||
import com.nukkitx.protocol.bedrock.data.AttributeData;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Getter;
|
||||
|
||||
@Getter
|
||||
@AllArgsConstructor
|
||||
public enum AttributeType {
|
||||
public enum GeyserAttributeType {
|
||||
|
||||
// Universal Attributes
|
||||
FOLLOW_RANGE("minecraft:generic.follow_range", "minecraft:follow_range", 0f, 2048f, 32f),
|
||||
@ -39,13 +40,13 @@ public enum AttributeType {
|
||||
FLYING_SPEED("minecraft:generic.flying_speed", "minecraft:movement", 0.0f, 1024.0f, 0.4000000059604645f),
|
||||
ATTACK_DAMAGE("minecraft:generic.attack_damage", "minecraft:attack_damage", 0f, 2048f, 1f),
|
||||
HORSE_JUMP_STRENGTH("minecraft:horse.jump_strength", "minecraft:horse.jump_strength", 0.0f, 2.0f, 0.7f),
|
||||
LUCK("minecraft:generic.luck", "minecraft:luck", -1024f, 1024f, 0f),
|
||||
|
||||
// Java Attributes
|
||||
ARMOR("minecraft:generic.armor", null, 0f, 30f, 0f),
|
||||
ARMOR_TOUGHNESS("minecraft:generic.armor_toughness", null, 0F, 20f, 0f),
|
||||
ATTACK_KNOCKBACK("minecraft:generic.attack_knockback", null, 1.5f, Float.MAX_VALUE, 0f),
|
||||
ATTACK_SPEED("minecraft:generic.attack_speed", null, 0f, 1024f, 4f),
|
||||
LUCK("minecraft:generic.luck", null, -1024f, 1024f, 0f),
|
||||
MAX_HEALTH("minecraft:generic.max_health", null, 0f, 1024f, 20f),
|
||||
|
||||
// Bedrock Attributes
|
||||
@ -64,19 +65,15 @@ public enum AttributeType {
|
||||
private final float maximum;
|
||||
private final float defaultValue;
|
||||
|
||||
public Attribute getAttribute(float value) {
|
||||
public AttributeData getAttribute(float value) {
|
||||
return getAttribute(value, maximum);
|
||||
}
|
||||
|
||||
public Attribute getAttribute(float value, float maximum) {
|
||||
return new Attribute(this, minimum, maximum, value, defaultValue);
|
||||
public AttributeData getAttribute(float value, float maximum) {
|
||||
if (bedrockIdentifier == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
public boolean isJavaAttribute() {
|
||||
return javaIdentifier != null;
|
||||
}
|
||||
|
||||
public boolean isBedrockAttribute() {
|
||||
return bedrockIdentifier != null;
|
||||
// Minimum, maximum, and default values are hardcoded on Java Edition, whereas controlled by the server on Bedrock
|
||||
return new AttributeData(bedrockIdentifier, minimum, maximum, value, defaultValue);
|
||||
}
|
||||
}
|
@ -25,20 +25,10 @@
|
||||
|
||||
package org.geysermc.connector.entity.living;
|
||||
|
||||
import com.github.steveice10.mc.protocol.data.game.entity.metadata.EntityMetadata;
|
||||
import com.nukkitx.math.vector.Vector3f;
|
||||
import com.nukkitx.protocol.bedrock.data.AttributeData;
|
||||
import com.nukkitx.protocol.bedrock.data.entity.EntityData;
|
||||
import com.nukkitx.protocol.bedrock.data.entity.EntityFlag;
|
||||
import com.nukkitx.protocol.bedrock.packet.UpdateAttributesPacket;
|
||||
import org.geysermc.connector.entity.attribute.AttributeType;
|
||||
import org.geysermc.connector.entity.type.EntityType;
|
||||
import org.geysermc.connector.network.session.GeyserSession;
|
||||
import org.geysermc.connector.utils.AttributeUtils;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
public class IronGolemEntity extends GolemEntity {
|
||||
|
||||
@ -49,32 +39,4 @@ public class IronGolemEntity extends GolemEntity {
|
||||
// Required, or else the overlay is black
|
||||
metadata.put(EntityData.COLOR_2, (byte) 0);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateBedrockMetadata(EntityMetadata entityMetadata, GeyserSession session) {
|
||||
super.updateBedrockMetadata(entityMetadata, session);
|
||||
if (entityMetadata.getId() == 9) {
|
||||
// Required so the resource pack sees the entity health
|
||||
attributes.put(AttributeType.HEALTH, AttributeType.HEALTH.getAttribute(metadata.getFloat(EntityData.HEALTH), 100f));
|
||||
updateBedrockAttributes(session);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateBedrockAttributes(GeyserSession session) {
|
||||
if (!valid) return;
|
||||
|
||||
List<AttributeData> attributes = new ArrayList<>();
|
||||
for (Map.Entry<AttributeType, org.geysermc.connector.entity.attribute.Attribute> entry : this.attributes.entrySet()) {
|
||||
if (!entry.getValue().getType().isBedrockAttribute())
|
||||
continue;
|
||||
|
||||
attributes.add(AttributeUtils.getBedrockAttribute(entry.getValue()));
|
||||
}
|
||||
|
||||
UpdateAttributesPacket updateAttributesPacket = new UpdateAttributesPacket();
|
||||
updateAttributesPacket.setRuntimeEntityId(geyserId);
|
||||
updateAttributesPacket.setAttributes(attributes);
|
||||
session.sendUpstreamPacket(updateAttributesPacket);
|
||||
}
|
||||
}
|
||||
|
@ -50,9 +50,4 @@ public class PigEntity extends AnimalEntity {
|
||||
public boolean canEat(GeyserSession session, String javaIdentifierStripped, ItemEntry itemEntry) {
|
||||
return javaIdentifierStripped.equals("carrot") || javaIdentifierStripped.equals("potato") || javaIdentifierStripped.equals("beetroot");
|
||||
}
|
||||
|
||||
@Override
|
||||
protected float getDefaultMaxHealth() {
|
||||
return 10f;
|
||||
}
|
||||
}
|
||||
|
@ -33,7 +33,8 @@ import com.nukkitx.protocol.bedrock.data.entity.EntityEventType;
|
||||
import com.nukkitx.protocol.bedrock.data.entity.EntityFlag;
|
||||
import com.nukkitx.protocol.bedrock.data.inventory.ContainerType;
|
||||
import com.nukkitx.protocol.bedrock.packet.EntityEventPacket;
|
||||
import org.geysermc.connector.entity.attribute.AttributeType;
|
||||
import com.nukkitx.protocol.bedrock.packet.UpdateAttributesPacket;
|
||||
import org.geysermc.connector.entity.attribute.GeyserAttributeType;
|
||||
import org.geysermc.connector.entity.living.animal.AnimalEntity;
|
||||
import org.geysermc.connector.entity.type.EntityType;
|
||||
import org.geysermc.connector.network.session.GeyserSession;
|
||||
@ -55,15 +56,24 @@ public class AbstractHorseEntity extends AnimalEntity {
|
||||
|
||||
// Specifies the size of the entity's inventory. Required to place slots in the entity.
|
||||
metadata.put(EntityData.CONTAINER_BASE_SIZE, 2);
|
||||
// Add dummy health attribute since LivingEntity updates the attribute for us
|
||||
attributes.put(AttributeType.HEALTH, AttributeType.HEALTH.getAttribute(20, 20));
|
||||
// Add horse jump strength attribute to allow donkeys and mules to jump
|
||||
attributes.put(AttributeType.HORSE_JUMP_STRENGTH, AttributeType.HORSE_JUMP_STRENGTH.getAttribute(0.5f, 2));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void spawnEntity(GeyserSession session) {
|
||||
super.spawnEntity(session);
|
||||
|
||||
// Add horse jump strength attribute to allow donkeys and mules to jump, if they don't send the attribute themselves.
|
||||
// Confirmed broken without this code by making a new donkey in vanilla 1.17.1
|
||||
// The spawn packet does have an attributes section, but adding the jump strength property there causes the
|
||||
// donkey to jump very high.
|
||||
UpdateAttributesPacket attributesPacket = new UpdateAttributesPacket();
|
||||
attributesPacket.setRuntimeEntityId(geyserId);
|
||||
attributesPacket.getAttributes().add(GeyserAttributeType.HORSE_JUMP_STRENGTH.getAttribute(0.5f, 2));
|
||||
session.sendUpstreamPacket(attributesPacket);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateBedrockMetadata(EntityMetadata entityMetadata, GeyserSession session) {
|
||||
|
||||
if (entityMetadata.getId() == 17) {
|
||||
byte xd = (byte) entityMetadata.getValue();
|
||||
metadata.getFlags().setFlag(EntityFlag.TAMED, (xd & 0x02) == 0x02);
|
||||
@ -105,11 +115,6 @@ public class AbstractHorseEntity extends AnimalEntity {
|
||||
metadata.getFlags().setFlag(EntityFlag.WASD_CONTROLLED, true);
|
||||
|
||||
super.updateBedrockMetadata(entityMetadata, session);
|
||||
|
||||
if (entityMetadata.getId() == 9) {
|
||||
// Update the health attribute
|
||||
updateBedrockAttributes(session);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -35,16 +35,12 @@ import com.nukkitx.protocol.bedrock.data.entity.EntityFlag;
|
||||
import com.nukkitx.protocol.bedrock.packet.*;
|
||||
import lombok.Data;
|
||||
import org.geysermc.connector.entity.Tickable;
|
||||
import org.geysermc.connector.entity.attribute.AttributeType;
|
||||
import org.geysermc.connector.entity.attribute.GeyserAttributeType;
|
||||
import org.geysermc.connector.entity.living.InsentientEntity;
|
||||
import org.geysermc.connector.entity.type.EntityType;
|
||||
import org.geysermc.connector.network.session.GeyserSession;
|
||||
import org.geysermc.connector.utils.AttributeUtils;
|
||||
import org.geysermc.connector.utils.DimensionUtils;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Random;
|
||||
import java.util.concurrent.ThreadLocalRandom;
|
||||
import java.util.concurrent.atomic.AtomicLong;
|
||||
@ -100,44 +96,21 @@ public class EnderDragonEntity extends InsentientEntity implements Tickable {
|
||||
|
||||
super.updateBedrockMetadata(entityMetadata, session);
|
||||
|
||||
if (entityMetadata.getId() == 9) { // Health
|
||||
// Update the health attribute, so that the death animation gets played
|
||||
// Round health up, so that Bedrock doesn't consider the dragon to be dead when health is between 0 and 1
|
||||
float health = (float) Math.ceil(metadata.getFloat(EntityData.HEALTH));
|
||||
if (phase == 9 && health <= 0) { // Dying phase
|
||||
if (entityMetadata.getId() == 9) {
|
||||
if (phase == 9 && this.health <= 0) { // Dying phase
|
||||
EntityEventPacket entityEventPacket = new EntityEventPacket();
|
||||
entityEventPacket.setType(EntityEventType.ENDER_DRAGON_DEATH);
|
||||
entityEventPacket.setRuntimeEntityId(geyserId);
|
||||
entityEventPacket.setData(0);
|
||||
session.sendUpstreamPacket(entityEventPacket);
|
||||
}
|
||||
attributes.put(AttributeType.HEALTH, AttributeType.HEALTH.getAttribute(health, 200));
|
||||
updateBedrockAttributes(session);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Send an updated list of attributes to the Bedrock client.
|
||||
* This is overwritten to allow the health attribute to differ from
|
||||
* the health specified in the metadata.
|
||||
*
|
||||
* @param session GeyserSession
|
||||
*/
|
||||
@Override
|
||||
public void updateBedrockAttributes(GeyserSession session) {
|
||||
if (!valid) return;
|
||||
|
||||
List<AttributeData> attributes = new ArrayList<>();
|
||||
for (Map.Entry<AttributeType, org.geysermc.connector.entity.attribute.Attribute> entry : this.attributes.entrySet()) {
|
||||
if (!entry.getValue().getType().isBedrockAttribute())
|
||||
continue;
|
||||
attributes.add(AttributeUtils.getBedrockAttribute(entry.getValue()));
|
||||
}
|
||||
|
||||
UpdateAttributesPacket updateAttributesPacket = new UpdateAttributesPacket();
|
||||
updateAttributesPacket.setRuntimeEntityId(geyserId);
|
||||
updateAttributesPacket.setAttributes(attributes);
|
||||
session.sendUpstreamPacket(updateAttributesPacket);
|
||||
protected AttributeData createHealthAttribute() {
|
||||
// Round health up, so that Bedrock doesn't consider the dragon to be dead when health is between 0 and 1
|
||||
return GeyserAttributeType.HEALTH.getAttribute((float) Math.ceil(this.health), this.maxHealth);
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -168,6 +141,13 @@ public class EnderDragonEntity extends InsentientEntity implements Tickable {
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addAdditionalSpawnData(AddEntityPacket addEntityPacket) {
|
||||
// Bedrock is EXTREMELY sensitive to the Ender Dragon's health - if it is dead once, it is dead for the rest of its life
|
||||
// Ensure that the first spawn packet sent has health data so this cannot happen until it actually should
|
||||
addEntityPacket.getAttributes().add(createHealthAttribute());
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean despawnEntity(GeyserSession session) {
|
||||
for (EnderDragonPartEntity part : allParts) {
|
||||
@ -318,7 +298,7 @@ public class EnderDragonEntity extends InsentientEntity implements Tickable {
|
||||
}
|
||||
|
||||
private boolean isAlive() {
|
||||
return metadata.getFloat(EntityData.HEALTH) > 0;
|
||||
return health > 0;
|
||||
}
|
||||
|
||||
private boolean isHovering() {
|
||||
|
@ -46,16 +46,14 @@ import lombok.Setter;
|
||||
import net.kyori.adventure.text.Component;
|
||||
import org.geysermc.connector.entity.Entity;
|
||||
import org.geysermc.connector.entity.LivingEntity;
|
||||
import org.geysermc.connector.entity.attribute.Attribute;
|
||||
import org.geysermc.connector.entity.attribute.AttributeType;
|
||||
import org.geysermc.connector.entity.living.animal.tameable.ParrotEntity;
|
||||
import org.geysermc.connector.entity.type.EntityType;
|
||||
import org.geysermc.connector.network.session.GeyserSession;
|
||||
import org.geysermc.connector.scoreboard.Team;
|
||||
import org.geysermc.connector.utils.AttributeUtils;
|
||||
import org.geysermc.connector.network.translators.chat.MessageTranslator;
|
||||
import org.geysermc.connector.scoreboard.Team;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.Collections;
|
||||
import java.util.UUID;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
@Getter @Setter
|
||||
@ -112,9 +110,6 @@ public class PlayerEntity extends LivingEntity {
|
||||
|
||||
valid = true;
|
||||
session.sendUpstreamPacket(addPlayerPacket);
|
||||
|
||||
updateAllEquipment(session);
|
||||
updateBedrockAttributes(session);
|
||||
}
|
||||
|
||||
public void sendPlayer(GeyserSession session) {
|
||||
@ -350,22 +345,4 @@ public class PlayerEntity extends LivingEntity {
|
||||
metadata.put(EntityData.BOUNDING_BOX_WIDTH, entityType.getWidth());
|
||||
metadata.put(EntityData.BOUNDING_BOX_HEIGHT, height);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateBedrockAttributes(GeyserSession session) { // TODO: Don't use duplicated code
|
||||
if (!valid) return;
|
||||
|
||||
List<AttributeData> attributes = new ArrayList<>();
|
||||
for (Map.Entry<AttributeType, Attribute> entry : this.attributes.entrySet()) {
|
||||
if (!entry.getValue().getType().isBedrockAttribute())
|
||||
continue;
|
||||
|
||||
attributes.add(AttributeUtils.getBedrockAttribute(entry.getValue()));
|
||||
}
|
||||
|
||||
UpdateAttributesPacket updateAttributesPacket = new UpdateAttributesPacket();
|
||||
updateAttributesPacket.setRuntimeEntityId(geyserId);
|
||||
updateAttributesPacket.setAttributes(attributes);
|
||||
session.sendUpstreamPacket(updateAttributesPacket);
|
||||
}
|
||||
}
|
||||
|
@ -26,18 +26,34 @@
|
||||
package org.geysermc.connector.entity.player;
|
||||
|
||||
import com.github.steveice10.mc.auth.data.GameProfile;
|
||||
import com.github.steveice10.mc.protocol.data.game.entity.attribute.Attribute;
|
||||
import com.github.steveice10.mc.protocol.data.game.entity.attribute.AttributeType;
|
||||
import com.github.steveice10.mc.protocol.data.game.entity.metadata.EntityMetadata;
|
||||
import com.github.steveice10.mc.protocol.data.game.entity.metadata.Pose;
|
||||
import com.nukkitx.math.vector.Vector3f;
|
||||
import com.nukkitx.protocol.bedrock.data.AttributeData;
|
||||
import com.nukkitx.protocol.bedrock.data.entity.EntityFlag;
|
||||
import com.nukkitx.protocol.bedrock.packet.UpdateAttributesPacket;
|
||||
import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap;
|
||||
import lombok.Getter;
|
||||
import org.geysermc.connector.entity.attribute.GeyserAttributeType;
|
||||
import org.geysermc.connector.network.session.GeyserSession;
|
||||
import org.geysermc.connector.utils.AttributeUtils;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* The entity class specifically for a {@link GeyserSession}'s player.
|
||||
*/
|
||||
public class SessionPlayerEntity extends PlayerEntity {
|
||||
/**
|
||||
* Used to fix some inconsistencies, especially in respawning.
|
||||
*/
|
||||
@Getter
|
||||
protected final Map<GeyserAttributeType, AttributeData> attributes = new Object2ObjectOpenHashMap<>();
|
||||
/**
|
||||
* Whether to check for updated speed after all entity metadata has been processed
|
||||
*/
|
||||
@ -83,14 +99,63 @@ public class SessionPlayerEntity extends PlayerEntity {
|
||||
}
|
||||
}
|
||||
|
||||
public float getMaxHealth() {
|
||||
return maxHealth;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setHealth(float health) {
|
||||
super.setHealth(health);
|
||||
}
|
||||
|
||||
@Override
|
||||
public AttributeData createHealthAttribute() {
|
||||
// Max health must be divisible by two in bedrock
|
||||
if ((maxHealth % 2) == 1) {
|
||||
maxHealth += 1;
|
||||
}
|
||||
return super.createHealthAttribute();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateBedrockMetadata(GeyserSession session) {
|
||||
super.updateBedrockMetadata(session);
|
||||
if (refreshSpeed) {
|
||||
if (session.adjustSpeed()) {
|
||||
updateBedrockAttributes(session);
|
||||
AttributeData speedAttribute = session.adjustSpeed();
|
||||
if (speedAttribute != null) {
|
||||
UpdateAttributesPacket attributesPacket = new UpdateAttributesPacket();
|
||||
attributesPacket.setRuntimeEntityId(geyserId);
|
||||
attributesPacket.setAttributes(Collections.singletonList(speedAttribute));
|
||||
session.sendUpstreamPacket(attributesPacket);
|
||||
}
|
||||
refreshSpeed = false;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void updateAttribute(Attribute javaAttribute, List<AttributeData> newAttributes) {
|
||||
if (javaAttribute.getType() == AttributeType.GENERIC_ATTACK_SPEED) {
|
||||
session.setAttackSpeed(AttributeUtils.calculateValue(javaAttribute));
|
||||
} else {
|
||||
super.updateAttribute(javaAttribute, newAttributes);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected AttributeData calculateAttribute(Attribute javaAttribute, GeyserAttributeType type) {
|
||||
AttributeData attributeData = super.calculateAttribute(javaAttribute, type);
|
||||
|
||||
if (javaAttribute.getType() == AttributeType.GENERIC_MOVEMENT_SPEED) {
|
||||
session.setOriginalSpeedAttribute(attributeData.getValue());
|
||||
AttributeData speedAttribute = session.adjustSpeed();
|
||||
if (speedAttribute != null) {
|
||||
// Overwrite the attribute with our own
|
||||
this.attributes.put(type, speedAttribute);
|
||||
return speedAttribute;
|
||||
}
|
||||
}
|
||||
|
||||
this.attributes.put(type, attributeData);
|
||||
return attributeData;
|
||||
}
|
||||
}
|
||||
|
@ -87,9 +87,6 @@ public class SkullPlayerEntity extends PlayerEntity {
|
||||
|
||||
valid = true;
|
||||
session.sendUpstreamPacket(addPlayerPacket);
|
||||
|
||||
updateAllEquipment(session);
|
||||
updateBedrockAttributes(session);
|
||||
}
|
||||
|
||||
public void despawnEntity(GeyserSession session, Vector3i position) {
|
||||
|
@ -78,8 +78,7 @@ import org.geysermc.connector.configuration.EmoteOffhandWorkaroundOption;
|
||||
import org.geysermc.connector.entity.Entity;
|
||||
import org.geysermc.connector.entity.ItemFrameEntity;
|
||||
import org.geysermc.connector.entity.Tickable;
|
||||
import org.geysermc.connector.entity.attribute.Attribute;
|
||||
import org.geysermc.connector.entity.attribute.AttributeType;
|
||||
import org.geysermc.connector.entity.attribute.GeyserAttributeType;
|
||||
import org.geysermc.connector.entity.player.SessionPlayerEntity;
|
||||
import org.geysermc.connector.entity.player.SkullPlayerEntity;
|
||||
import org.geysermc.connector.inventory.Inventory;
|
||||
@ -880,9 +879,13 @@ public class GeyserSession implements CommandSender {
|
||||
this.sneaking = sneaking;
|
||||
|
||||
// Update pose and bounding box on our end
|
||||
if (!sneaking && adjustSpeed()) {
|
||||
AttributeData speedAttribute;
|
||||
if (!sneaking && (speedAttribute = adjustSpeed()) != null) {
|
||||
// Update attributes since we're still "sneaking" under a 1.5-block-tall area
|
||||
playerEntity.updateBedrockAttributes(this);
|
||||
UpdateAttributesPacket attributesPacket = new UpdateAttributesPacket();
|
||||
attributesPacket.setRuntimeEntityId(playerEntity.getGeyserId());
|
||||
attributesPacket.setAttributes(Collections.singletonList(speedAttribute));
|
||||
sendUpstreamPacket(attributesPacket);
|
||||
// the server *should* update our pose once it has returned to normal
|
||||
} else {
|
||||
if (!flying) {
|
||||
@ -928,23 +931,25 @@ public class GeyserSession implements CommandSender {
|
||||
/**
|
||||
* Adjusts speed if the player is crawling.
|
||||
*
|
||||
* @return true if attributes should be updated.
|
||||
* @return not null if attributes should be updated.
|
||||
*/
|
||||
public boolean adjustSpeed() {
|
||||
Attribute currentPlayerSpeed = playerEntity.getAttributes().get(AttributeType.MOVEMENT_SPEED);
|
||||
public AttributeData adjustSpeed() {
|
||||
AttributeData currentPlayerSpeed = playerEntity.getAttributes().get(GeyserAttributeType.MOVEMENT_SPEED);
|
||||
if (currentPlayerSpeed != null) {
|
||||
if ((pose.equals(Pose.SNEAKING) && !sneaking && collisionManager.isUnderSlab()) ||
|
||||
(!swimmingInWater && playerEntity.getMetadata().getFlags().getFlag(EntityFlag.SWIMMING) && !collisionManager.isPlayerInWater())) {
|
||||
// Either of those conditions means that Bedrock goes zoom when they shouldn't be
|
||||
currentPlayerSpeed.setValue(originalSpeedAttribute / 3.32f);
|
||||
return true;
|
||||
AttributeData speedAttribute = GeyserAttributeType.MOVEMENT_SPEED.getAttribute(originalSpeedAttribute / 3.32f);
|
||||
playerEntity.getAttributes().put(GeyserAttributeType.MOVEMENT_SPEED, speedAttribute);
|
||||
return speedAttribute;
|
||||
} else if (originalSpeedAttribute != currentPlayerSpeed.getValue()) {
|
||||
// Speed has reset to normal
|
||||
currentPlayerSpeed.setValue(originalSpeedAttribute);
|
||||
return true;
|
||||
AttributeData speedAttribute = GeyserAttributeType.MOVEMENT_SPEED.getAttribute(originalSpeedAttribute);
|
||||
playerEntity.getAttributes().put(GeyserAttributeType.MOVEMENT_SPEED, speedAttribute);
|
||||
return speedAttribute;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -26,14 +26,13 @@
|
||||
package org.geysermc.connector.network.translators.bedrock;
|
||||
|
||||
import com.github.steveice10.mc.protocol.packet.ingame.client.ClientKeepAlivePacket;
|
||||
import com.nukkitx.protocol.bedrock.data.AttributeData;
|
||||
import com.nukkitx.protocol.bedrock.packet.NetworkStackLatencyPacket;
|
||||
import com.nukkitx.protocol.bedrock.packet.UpdateAttributesPacket;
|
||||
import org.geysermc.connector.entity.attribute.Attribute;
|
||||
import org.geysermc.connector.entity.attribute.AttributeType;
|
||||
import org.geysermc.connector.entity.attribute.GeyserAttributeType;
|
||||
import org.geysermc.connector.network.session.GeyserSession;
|
||||
import org.geysermc.connector.network.translators.PacketTranslator;
|
||||
import org.geysermc.connector.network.translators.Translator;
|
||||
import org.geysermc.connector.utils.AttributeUtils;
|
||||
import org.geysermc.floodgate.util.DeviceOs;
|
||||
|
||||
import java.util.Collections;
|
||||
@ -70,11 +69,11 @@ public class BedrockNetworkStackLatencyTranslator extends PacketTranslator<Netwo
|
||||
UpdateAttributesPacket attributesPacket = new UpdateAttributesPacket();
|
||||
attributesPacket.setRuntimeEntityId(session.getPlayerEntity().getGeyserId());
|
||||
|
||||
Attribute attribute = session.getPlayerEntity().getAttributes().get(AttributeType.EXPERIENCE_LEVEL);
|
||||
AttributeData attribute = session.getPlayerEntity().getAttributes().get(GeyserAttributeType.EXPERIENCE_LEVEL);
|
||||
if (attribute != null) {
|
||||
attributesPacket.setAttributes(Collections.singletonList(AttributeUtils.getBedrockAttribute(attribute)));
|
||||
attributesPacket.setAttributes(Collections.singletonList(attribute));
|
||||
} else {
|
||||
attributesPacket.setAttributes(Collections.singletonList(AttributeUtils.getBedrockAttribute(AttributeType.EXPERIENCE_LEVEL.getAttribute(0))));
|
||||
attributesPacket.setAttributes(Collections.singletonList(GeyserAttributeType.EXPERIENCE_LEVEL.getAttribute(0)));
|
||||
}
|
||||
|
||||
session.getConnector().getGeneralThreadPool().schedule(
|
||||
|
@ -35,12 +35,10 @@ import com.nukkitx.protocol.bedrock.data.LevelEventType;
|
||||
import com.nukkitx.protocol.bedrock.data.PlayerActionType;
|
||||
import com.nukkitx.protocol.bedrock.data.entity.EntityEventType;
|
||||
import com.nukkitx.protocol.bedrock.data.entity.EntityFlag;
|
||||
import com.nukkitx.protocol.bedrock.packet.EntityEventPacket;
|
||||
import com.nukkitx.protocol.bedrock.packet.LevelEventPacket;
|
||||
import com.nukkitx.protocol.bedrock.packet.PlayStatusPacket;
|
||||
import com.nukkitx.protocol.bedrock.packet.PlayerActionPacket;
|
||||
import com.nukkitx.protocol.bedrock.packet.*;
|
||||
import org.geysermc.connector.entity.Entity;
|
||||
import org.geysermc.connector.entity.ItemFrameEntity;
|
||||
import org.geysermc.connector.entity.player.SessionPlayerEntity;
|
||||
import org.geysermc.connector.inventory.PlayerInventory;
|
||||
import org.geysermc.connector.network.session.GeyserSession;
|
||||
import org.geysermc.connector.network.translators.PacketTranslator;
|
||||
@ -49,12 +47,14 @@ import org.geysermc.connector.network.translators.item.ItemRegistry;
|
||||
import org.geysermc.connector.network.translators.world.block.BlockTranslator;
|
||||
import org.geysermc.connector.utils.BlockUtils;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
||||
@Translator(packet = PlayerActionPacket.class)
|
||||
public class BedrockActionTranslator extends PacketTranslator<PlayerActionPacket> {
|
||||
|
||||
@Override
|
||||
public void translate(PlayerActionPacket packet, GeyserSession session) {
|
||||
Entity entity = session.getPlayerEntity();
|
||||
SessionPlayerEntity entity = session.getPlayerEntity();
|
||||
|
||||
// Send book update before any player action
|
||||
if (packet.getAction() != PlayerActionType.RESPAWN) {
|
||||
@ -73,7 +73,10 @@ public class BedrockActionTranslator extends PacketTranslator<PlayerActionPacket
|
||||
eventPacket.setData(0);
|
||||
session.sendUpstreamPacket(eventPacket);
|
||||
// Resend attributes or else in rare cases the user can think they're not dead when they are, upon joining the server
|
||||
entity.updateBedrockAttributes(session);
|
||||
UpdateAttributesPacket attributesPacket = new UpdateAttributesPacket();
|
||||
attributesPacket.setRuntimeEntityId(entity.getGeyserId());
|
||||
attributesPacket.setAttributes(new ArrayList<>(entity.getAttributes().values()));
|
||||
session.sendUpstreamPacket(attributesPacket);
|
||||
break;
|
||||
case START_SWIMMING:
|
||||
ClientPlayerStatePacket startSwimPacket = new ClientPlayerStatePacket((int) entity.getEntityId(), PlayerState.START_SPRINTING);
|
||||
@ -235,7 +238,12 @@ public class BedrockActionTranslator extends PacketTranslator<PlayerActionPacket
|
||||
PlayStatusPacket spawnPacket = new PlayStatusPacket();
|
||||
spawnPacket.setStatus(PlayStatusPacket.Status.PLAYER_SPAWN);
|
||||
session.sendUpstreamPacket(spawnPacket);
|
||||
entity.updateBedrockAttributes(session);
|
||||
|
||||
attributesPacket = new UpdateAttributesPacket();
|
||||
attributesPacket.setRuntimeEntityId(entity.getGeyserId());
|
||||
attributesPacket.setAttributes(new ArrayList<>(entity.getAttributes().values()));
|
||||
session.sendUpstreamPacket(attributesPacket);
|
||||
|
||||
session.getEntityCache().updateBossBars();
|
||||
break;
|
||||
case JUMP:
|
||||
|
@ -30,8 +30,8 @@ import com.nukkitx.math.vector.Vector3f;
|
||||
import com.nukkitx.protocol.bedrock.data.LevelEventType;
|
||||
import com.nukkitx.protocol.bedrock.packet.LevelEventPacket;
|
||||
import com.nukkitx.protocol.bedrock.packet.SetPlayerGameTypePacket;
|
||||
import org.geysermc.connector.entity.Entity;
|
||||
import org.geysermc.connector.entity.attribute.AttributeType;
|
||||
import org.geysermc.connector.entity.attribute.GeyserAttributeType;
|
||||
import org.geysermc.connector.entity.player.SessionPlayerEntity;
|
||||
import org.geysermc.connector.network.session.GeyserSession;
|
||||
import org.geysermc.connector.network.translators.PacketTranslator;
|
||||
import org.geysermc.connector.network.translators.Translator;
|
||||
@ -44,11 +44,10 @@ public class JavaRespawnTranslator extends PacketTranslator<ServerRespawnPacket>
|
||||
|
||||
@Override
|
||||
public void translate(ServerRespawnPacket packet, GeyserSession session) {
|
||||
Entity entity = session.getPlayerEntity();
|
||||
SessionPlayerEntity entity = session.getPlayerEntity();
|
||||
|
||||
float maxHealth = entity.getAttributes().containsKey(AttributeType.MAX_HEALTH) ? entity.getAttributes().get(AttributeType.MAX_HEALTH).getValue() : 20f;
|
||||
// Max health must be divisible by two in bedrock
|
||||
entity.getAttributes().put(AttributeType.HEALTH, AttributeType.HEALTH.getAttribute(maxHealth, (maxHealth % 2 == 1 ? maxHealth + 1 : maxHealth)));
|
||||
entity.setHealth(entity.getMaxHealth());
|
||||
entity.getAttributes().put(GeyserAttributeType.HEALTH, entity.createHealthAttribute());
|
||||
|
||||
session.addInventoryTask(() -> {
|
||||
session.setInventoryTranslator(InventoryTranslator.PLAYER_INVENTORY_TRANSLATOR);
|
||||
|
@ -25,67 +25,26 @@
|
||||
|
||||
package org.geysermc.connector.network.translators.java.entity;
|
||||
|
||||
import com.github.steveice10.mc.protocol.data.game.entity.attribute.Attribute;
|
||||
import com.github.steveice10.mc.protocol.packet.ingame.server.entity.ServerEntityPropertiesPacket;
|
||||
import org.geysermc.connector.entity.Entity;
|
||||
import org.geysermc.connector.entity.attribute.AttributeType;
|
||||
import org.geysermc.connector.entity.LivingEntity;
|
||||
import org.geysermc.connector.network.session.GeyserSession;
|
||||
import org.geysermc.connector.network.translators.PacketTranslator;
|
||||
import org.geysermc.connector.network.translators.Translator;
|
||||
import org.geysermc.connector.utils.AttributeUtils;
|
||||
|
||||
@Translator(packet = ServerEntityPropertiesPacket.class)
|
||||
public class JavaEntityPropertiesTranslator extends PacketTranslator<ServerEntityPropertiesPacket> {
|
||||
|
||||
@Override
|
||||
public void translate(ServerEntityPropertiesPacket packet, GeyserSession session) {
|
||||
boolean isSessionPlayer = false;
|
||||
Entity entity = session.getEntityCache().getEntityByJavaId(packet.getEntityId());
|
||||
Entity entity;
|
||||
if (packet.getEntityId() == session.getPlayerEntity().getEntityId()) {
|
||||
entity = session.getPlayerEntity();
|
||||
isSessionPlayer = true;
|
||||
} else {
|
||||
entity = session.getEntityCache().getEntityByJavaId(packet.getEntityId());
|
||||
}
|
||||
if (entity == null) return;
|
||||
if (!(entity instanceof LivingEntity)) return;
|
||||
|
||||
for (Attribute attribute : packet.getAttributes()) {
|
||||
switch (attribute.getType()) {
|
||||
case GENERIC_MAX_HEALTH:
|
||||
entity.getAttributes().put(AttributeType.MAX_HEALTH, AttributeType.MAX_HEALTH.getAttribute((float) AttributeUtils.calculateValue(attribute)));
|
||||
break;
|
||||
case GENERIC_ATTACK_DAMAGE:
|
||||
entity.getAttributes().put(AttributeType.ATTACK_DAMAGE, AttributeType.ATTACK_DAMAGE.getAttribute((float) AttributeUtils.calculateValue(attribute)));
|
||||
break;
|
||||
case GENERIC_ATTACK_SPEED:
|
||||
if (isSessionPlayer) {
|
||||
// Get attack speed value for use in sending the faux cooldown
|
||||
double attackSpeed = AttributeUtils.calculateValue(attribute);
|
||||
session.setAttackSpeed(attackSpeed);
|
||||
}
|
||||
break;
|
||||
case GENERIC_FLYING_SPEED:
|
||||
entity.getAttributes().put(AttributeType.FLYING_SPEED, AttributeType.FLYING_SPEED.getAttribute((float) AttributeUtils.calculateValue(attribute)));
|
||||
entity.getAttributes().put(AttributeType.MOVEMENT_SPEED, AttributeType.MOVEMENT_SPEED.getAttribute((float) AttributeUtils.calculateValue(attribute)));
|
||||
break;
|
||||
case GENERIC_MOVEMENT_SPEED:
|
||||
float value = (float) AttributeUtils.calculateValue(attribute);
|
||||
entity.getAttributes().put(AttributeType.MOVEMENT_SPEED, AttributeType.MOVEMENT_SPEED.getAttribute(value));
|
||||
if (isSessionPlayer) {
|
||||
session.setOriginalSpeedAttribute(value);
|
||||
session.adjustSpeed();
|
||||
}
|
||||
break;
|
||||
case GENERIC_FOLLOW_RANGE:
|
||||
entity.getAttributes().put(AttributeType.FOLLOW_RANGE, AttributeType.FOLLOW_RANGE.getAttribute((float) AttributeUtils.calculateValue(attribute)));
|
||||
break;
|
||||
case GENERIC_KNOCKBACK_RESISTANCE:
|
||||
entity.getAttributes().put(AttributeType.KNOCKBACK_RESISTANCE, AttributeType.KNOCKBACK_RESISTANCE.getAttribute((float) AttributeUtils.calculateValue(attribute)));
|
||||
break;
|
||||
case HORSE_JUMP_STRENGTH:
|
||||
entity.getAttributes().put(AttributeType.HORSE_JUMP_STRENGTH, AttributeType.HORSE_JUMP_STRENGTH.getAttribute((float) AttributeUtils.calculateValue(attribute)));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
entity.updateBedrockAttributes(session);
|
||||
((LivingEntity) entity).updateBedrockAttributes(session, packet.getAttributes());
|
||||
}
|
||||
}
|
||||
|
@ -25,38 +25,48 @@
|
||||
|
||||
package org.geysermc.connector.network.translators.java.entity.player;
|
||||
|
||||
import org.geysermc.connector.entity.Entity;
|
||||
import org.geysermc.connector.entity.attribute.AttributeType;
|
||||
import com.github.steveice10.mc.protocol.packet.ingame.server.entity.player.ServerPlayerHealthPacket;
|
||||
import com.nukkitx.protocol.bedrock.data.AttributeData;
|
||||
import com.nukkitx.protocol.bedrock.packet.SetHealthPacket;
|
||||
import com.nukkitx.protocol.bedrock.packet.UpdateAttributesPacket;
|
||||
import org.geysermc.connector.entity.attribute.GeyserAttributeType;
|
||||
import org.geysermc.connector.entity.player.SessionPlayerEntity;
|
||||
import org.geysermc.connector.network.session.GeyserSession;
|
||||
import org.geysermc.connector.network.translators.PacketTranslator;
|
||||
import org.geysermc.connector.network.translators.Translator;
|
||||
|
||||
import com.github.steveice10.mc.protocol.packet.ingame.server.entity.player.ServerPlayerHealthPacket;
|
||||
import com.nukkitx.protocol.bedrock.packet.SetHealthPacket;
|
||||
import java.util.List;
|
||||
|
||||
@Translator(packet = ServerPlayerHealthPacket.class)
|
||||
public class JavaPlayerHealthTranslator extends PacketTranslator<ServerPlayerHealthPacket> {
|
||||
|
||||
@Override
|
||||
public void translate(ServerPlayerHealthPacket packet, GeyserSession session) {
|
||||
Entity entity = session.getPlayerEntity();
|
||||
if (entity == null)
|
||||
return;
|
||||
SessionPlayerEntity entity = session.getPlayerEntity();
|
||||
|
||||
int health = (int) Math.ceil(packet.getHealth());
|
||||
SetHealthPacket setHealthPacket = new SetHealthPacket();
|
||||
setHealthPacket.setHealth(health);
|
||||
session.sendUpstreamPacket(setHealthPacket);
|
||||
|
||||
float maxHealth = entity.getAttributes().containsKey(AttributeType.MAX_HEALTH) ? entity.getAttributes().get(AttributeType.MAX_HEALTH).getValue() : 20f;
|
||||
// Max health must be divisible by two in bedrock
|
||||
if ((maxHealth % 2) == 1) {
|
||||
maxHealth += 1;
|
||||
}
|
||||
entity.setHealth(packet.getHealth());
|
||||
|
||||
entity.getAttributes().put(AttributeType.HEALTH, AttributeType.HEALTH.getAttribute(health, maxHealth));
|
||||
entity.getAttributes().put(AttributeType.HUNGER, AttributeType.HUNGER.getAttribute(packet.getFood()));
|
||||
entity.getAttributes().put(AttributeType.SATURATION, AttributeType.SATURATION.getAttribute(packet.getSaturation()));
|
||||
entity.updateBedrockAttributes(session);
|
||||
UpdateAttributesPacket attributesPacket = new UpdateAttributesPacket();
|
||||
List<AttributeData> attributes = attributesPacket.getAttributes();
|
||||
|
||||
AttributeData healthAttribute = entity.createHealthAttribute();
|
||||
entity.getAttributes().put(GeyserAttributeType.HEALTH, healthAttribute);
|
||||
attributes.add(healthAttribute);
|
||||
|
||||
AttributeData hungerAttribute = GeyserAttributeType.HUNGER.getAttribute(packet.getFood());
|
||||
entity.getAttributes().put(GeyserAttributeType.HUNGER, hungerAttribute);
|
||||
attributes.add(hungerAttribute);
|
||||
|
||||
AttributeData saturationAttribute = GeyserAttributeType.SATURATION.getAttribute(packet.getSaturation());
|
||||
entity.getAttributes().put(GeyserAttributeType.SATURATION, saturationAttribute);
|
||||
attributes.add(saturationAttribute);
|
||||
|
||||
attributesPacket.setRuntimeEntityId(entity.getGeyserId());
|
||||
session.sendUpstreamPacket(attributesPacket);
|
||||
}
|
||||
}
|
||||
|
@ -25,25 +25,32 @@
|
||||
|
||||
package org.geysermc.connector.network.translators.java.entity.player;
|
||||
|
||||
import org.geysermc.connector.entity.Entity;
|
||||
import org.geysermc.connector.entity.attribute.AttributeType;
|
||||
import com.github.steveice10.mc.protocol.packet.ingame.server.entity.player.ServerPlayerSetExperiencePacket;
|
||||
import com.nukkitx.protocol.bedrock.data.AttributeData;
|
||||
import com.nukkitx.protocol.bedrock.packet.UpdateAttributesPacket;
|
||||
import org.geysermc.connector.entity.attribute.GeyserAttributeType;
|
||||
import org.geysermc.connector.entity.player.SessionPlayerEntity;
|
||||
import org.geysermc.connector.network.session.GeyserSession;
|
||||
import org.geysermc.connector.network.translators.PacketTranslator;
|
||||
import org.geysermc.connector.network.translators.Translator;
|
||||
|
||||
import com.github.steveice10.mc.protocol.packet.ingame.server.entity.player.ServerPlayerSetExperiencePacket;
|
||||
import java.util.Arrays;
|
||||
|
||||
@Translator(packet = ServerPlayerSetExperiencePacket.class)
|
||||
public class JavaPlayerSetExperienceTranslator extends PacketTranslator<ServerPlayerSetExperiencePacket> {
|
||||
|
||||
@Override
|
||||
public void translate(ServerPlayerSetExperiencePacket packet, GeyserSession session) {
|
||||
Entity entity = session.getPlayerEntity();
|
||||
if (entity == null)
|
||||
return;
|
||||
SessionPlayerEntity entity = session.getPlayerEntity();
|
||||
|
||||
entity.getAttributes().put(AttributeType.EXPERIENCE, AttributeType.EXPERIENCE.getAttribute(packet.getExperience()));
|
||||
entity.getAttributes().put(AttributeType.EXPERIENCE_LEVEL, AttributeType.EXPERIENCE_LEVEL.getAttribute(packet.getLevel()));
|
||||
entity.updateBedrockAttributes(session);
|
||||
AttributeData experience = GeyserAttributeType.EXPERIENCE.getAttribute(packet.getExperience());
|
||||
entity.getAttributes().put(GeyserAttributeType.EXPERIENCE, experience);
|
||||
AttributeData experienceLevel = GeyserAttributeType.EXPERIENCE_LEVEL.getAttribute(packet.getLevel());
|
||||
entity.getAttributes().put(GeyserAttributeType.EXPERIENCE_LEVEL, experienceLevel);
|
||||
|
||||
UpdateAttributesPacket attributesPacket = new UpdateAttributesPacket();
|
||||
attributesPacket.setRuntimeEntityId(session.getPlayerEntity().getGeyserId());
|
||||
attributesPacket.setAttributes(Arrays.asList(experience, experienceLevel));
|
||||
session.sendUpstreamPacket(attributesPacket);
|
||||
}
|
||||
}
|
||||
|
@ -60,9 +60,7 @@ public class JavaSpawnPlayerTranslator extends PacketTranslator<ServerSpawnPlaye
|
||||
}
|
||||
session.getEntityCache().cacheEntity(entity);
|
||||
|
||||
if (session.getUpstream().isInitialized()) {
|
||||
entity.sendPlayer(session);
|
||||
SkinManager.requestAndHandleSkinAndCape(entity, session, null);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -25,59 +25,18 @@
|
||||
|
||||
package org.geysermc.connector.utils;
|
||||
|
||||
import com.github.steveice10.mc.protocol.data.game.entity.attribute.Attribute;
|
||||
import com.github.steveice10.mc.protocol.data.game.entity.attribute.AttributeModifier;
|
||||
import com.github.steveice10.mc.protocol.data.game.entity.attribute.ModifierOperation;
|
||||
import com.nukkitx.protocol.bedrock.data.AttributeData;
|
||||
import org.geysermc.connector.entity.attribute.Attribute;
|
||||
import org.geysermc.connector.entity.attribute.AttributeType;
|
||||
|
||||
public class AttributeUtils {
|
||||
|
||||
public static com.github.steveice10.mc.protocol.data.game.entity.attribute.Attribute getJavaAttribute(Attribute attribute) {
|
||||
if (!attribute.getType().isJavaAttribute())
|
||||
return null;
|
||||
|
||||
com.github.steveice10.mc.protocol.data.game.entity.attribute.AttributeType type = null;
|
||||
try {
|
||||
type = com.github.steveice10.mc.protocol.data.game.entity.attribute.AttributeType.valueOf("GENERIC_" + attribute.getType().name());
|
||||
} catch (Exception ex) {
|
||||
// Catch and loop since attributes can semi-overlap
|
||||
for (AttributeType attributeType : AttributeType.values()) {
|
||||
if (!attributeType.isJavaAttribute())
|
||||
continue;
|
||||
|
||||
if (!attributeType.getJavaIdentifier().equals(attribute.getType().getJavaIdentifier()))
|
||||
continue;
|
||||
|
||||
try {
|
||||
type = com.github.steveice10.mc.protocol.data.game.entity.attribute.AttributeType.valueOf("GENERIC_" + attributeType.name());
|
||||
} catch (Exception e) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (type == null)
|
||||
return null;
|
||||
|
||||
return new com.github.steveice10.mc.protocol.data.game.entity.attribute.Attribute(type, attribute.getValue());
|
||||
}
|
||||
|
||||
public static AttributeData getBedrockAttribute(Attribute attribute) {
|
||||
AttributeType type = attribute.getType();
|
||||
if (!type.isBedrockAttribute())
|
||||
return null;
|
||||
|
||||
return new AttributeData(type.getBedrockIdentifier(), attribute.getMinimum(), attribute.getMaximum(), attribute.getValue(), attribute.getDefaultValue());
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve the base attribute value with all modifiers applied.
|
||||
* https://minecraft.gamepedia.com/Attribute#Modifiers
|
||||
* @param attribute The attribute to calculate the total value.
|
||||
* @return The finished attribute with all modifiers applied.
|
||||
*/
|
||||
public static double calculateValue(com.github.steveice10.mc.protocol.data.game.entity.attribute.Attribute attribute) {
|
||||
public static double calculateValue(Attribute attribute) {
|
||||
double base = attribute.getValue();
|
||||
for (AttributeModifier modifier : attribute.getModifiers()) {
|
||||
if (modifier.getOperation() == ModifierOperation.ADD) {
|
||||
|
Laden…
In neuem Issue referenzieren
Einen Benutzer sperren