diff --git a/connector/src/main/java/org/geysermc/connector/GeyserConnector.java b/connector/src/main/java/org/geysermc/connector/GeyserConnector.java index fd2432a4c..a33c51952 100644 --- a/connector/src/main/java/org/geysermc/connector/GeyserConnector.java +++ b/connector/src/main/java/org/geysermc/connector/GeyserConnector.java @@ -44,6 +44,7 @@ import org.geysermc.connector.bootstrap.GeyserBootstrap; import org.geysermc.connector.command.CommandManager; import org.geysermc.connector.common.AuthType; import org.geysermc.connector.configuration.GeyserConfiguration; +import org.geysermc.connector.entity.EntityDefinitions; import org.geysermc.connector.metrics.Metrics; import org.geysermc.connector.network.ConnectorServerEventHandler; import org.geysermc.connector.network.session.GeyserSession; @@ -153,6 +154,7 @@ public class GeyserConnector { BlockRegistries.init(); Registries.init(); + EntityDefinitions.init(); ItemTranslator.init(); MessageTranslator.init(); LocaleUtils.init(); diff --git a/connector/src/main/java/org/geysermc/connector/command/defaults/OffhandCommand.java b/connector/src/main/java/org/geysermc/connector/command/defaults/OffhandCommand.java index ea6062063..06d554ece 100644 --- a/connector/src/main/java/org/geysermc/connector/command/defaults/OffhandCommand.java +++ b/connector/src/main/java/org/geysermc/connector/command/defaults/OffhandCommand.java @@ -25,8 +25,8 @@ package org.geysermc.connector.command.defaults; +import com.github.steveice10.mc.protocol.data.game.entity.object.Direction; import com.github.steveice10.mc.protocol.data.game.entity.player.PlayerAction; -import com.github.steveice10.mc.protocol.data.game.level.block.BlockFace; import com.github.steveice10.mc.protocol.packet.ingame.serverbound.player.ServerboundPlayerActionPacket; import org.geysermc.connector.GeyserConnector; import org.geysermc.connector.command.CommandSender; @@ -47,7 +47,7 @@ public class OffhandCommand extends GeyserCommand { } ServerboundPlayerActionPacket releaseItemPacket = new ServerboundPlayerActionPacket(PlayerAction.SWAP_HANDS, BlockUtils.POSITION_ZERO, - BlockFace.DOWN); + Direction.DOWN); session.sendDownstreamPacket(releaseItemPacket); } diff --git a/connector/src/main/java/org/geysermc/connector/entity/AbstractArrowEntity.java b/connector/src/main/java/org/geysermc/connector/entity/AbstractArrowEntity.java index 54bec7257..f51516dc6 100644 --- a/connector/src/main/java/org/geysermc/connector/entity/AbstractArrowEntity.java +++ b/connector/src/main/java/org/geysermc/connector/entity/AbstractArrowEntity.java @@ -26,37 +26,43 @@ package org.geysermc.connector.entity; import com.github.steveice10.mc.protocol.data.game.entity.metadata.EntityMetadata; +import com.github.steveice10.mc.protocol.data.game.entity.metadata.type.ByteEntityMetadata; +import com.github.steveice10.mc.protocol.data.game.entity.type.EntityType; import com.nukkitx.math.vector.Vector3f; import com.nukkitx.protocol.bedrock.data.entity.EntityFlag; -import org.geysermc.connector.entity.type.EntityType; import org.geysermc.connector.network.session.GeyserSession; +import java.util.UUID; + public class AbstractArrowEntity extends Entity { - public AbstractArrowEntity(long entityId, long geyserId, EntityType entityType, Vector3f position, Vector3f motion, Vector3f rotation) { - super(entityId, geyserId, entityType, position, motion, rotation); + public AbstractArrowEntity(GeyserSession session, long 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); // Set the correct texture if using the resource pack - metadata.getFlags().setFlag(EntityFlag.BRIBED, entityType == EntityType.SPECTRAL_ARROW); + dirtyMetadata.getFlags().setFlag(EntityFlag.BRIBED, definition.entityType() == EntityType.SPECTRAL_ARROW); setMotion(motion); } + public void setArrowFlags(EntityMetadata entityMetadata) { + byte data = ((ByteEntityMetadata) entityMetadata).getPrimitiveValue(); + + setFlag(EntityFlag.CRITICAL, (data & 0x01) == 0x01); + } + + // Ignore the rotation sent by the Java server since the + // Java client calculates the rotation from the motion @Override - public void updateBedrockMetadata(EntityMetadata entityMetadata, GeyserSession session) { - if (entityMetadata.getId() == 8) { - byte data = (byte) entityMetadata.getValue(); - - metadata.getFlags().setFlag(EntityFlag.CRITICAL, (data & 0x01) == 0x01); - } - - super.updateBedrockMetadata(entityMetadata, session); + public void setYaw(float yaw) { } @Override - public void setRotation(Vector3f rotation) { - // Ignore the rotation sent by the Java server since the - // Java client calculates the rotation from the motion + public void setPitch(float pitch) { + } + + @Override + public void setHeadYaw(float headYaw) { } @Override @@ -64,8 +70,8 @@ public class AbstractArrowEntity extends Entity { super.setMotion(motion); double horizontalSpeed = Math.sqrt(motion.getX() * motion.getX() + motion.getZ() * motion.getZ()); - float yaw = (float) Math.toDegrees(Math.atan2(motion.getX(), motion.getZ())); - float pitch = (float) Math.toDegrees(Math.atan2(motion.getY(), horizontalSpeed)); - rotation = Vector3f.from(yaw, pitch, yaw); + this.yaw = (float) Math.toDegrees(Math.atan2(motion.getX(), motion.getZ())); + this.pitch = (float) Math.toDegrees(Math.atan2(motion.getY(), horizontalSpeed)); + this.headYaw = yaw; } } diff --git a/connector/src/main/java/org/geysermc/connector/entity/AreaEffectCloudEntity.java b/connector/src/main/java/org/geysermc/connector/entity/AreaEffectCloudEntity.java index 48283818e..1c2830232 100644 --- a/connector/src/main/java/org/geysermc/connector/entity/AreaEffectCloudEntity.java +++ b/connector/src/main/java/org/geysermc/connector/entity/AreaEffectCloudEntity.java @@ -26,44 +26,47 @@ package org.geysermc.connector.entity; import com.github.steveice10.mc.protocol.data.game.entity.metadata.EntityMetadata; +import com.github.steveice10.mc.protocol.data.game.entity.metadata.type.FloatEntityMetadata; import com.github.steveice10.mc.protocol.data.game.level.particle.Particle; import com.nukkitx.math.vector.Vector3f; import com.nukkitx.protocol.bedrock.data.entity.EntityData; import com.nukkitx.protocol.bedrock.data.entity.EntityFlag; -import org.geysermc.connector.entity.type.EntityType; import org.geysermc.connector.network.session.GeyserSession; import org.geysermc.connector.utils.EffectUtils; +import java.util.UUID; + public class AreaEffectCloudEntity extends Entity { - public AreaEffectCloudEntity(long entityId, long geyserId, EntityType entityType, Vector3f position, Vector3f motion, Vector3f rotation) { - super(entityId, geyserId, entityType, position, motion, rotation); - - // Without this the cloud doesn't appear, - metadata.put(EntityData.AREA_EFFECT_CLOUD_DURATION, 600); - - // This disabled client side shrink of the cloud - metadata.put(EntityData.AREA_EFFECT_CLOUD_RADIUS, 0.0f); - metadata.put(EntityData.AREA_EFFECT_CLOUD_CHANGE_RATE, -0.005f); - metadata.put(EntityData.AREA_EFFECT_CLOUD_CHANGE_ON_PICKUP, -0.5f); - - metadata.getFlags().setFlag(EntityFlag.FIRE_IMMUNE, true); + public AreaEffectCloudEntity(GeyserSession session, long 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 - public void updateBedrockMetadata(EntityMetadata entityMetadata, GeyserSession session) { - if (entityMetadata.getId() == 8) { - metadata.put(EntityData.AREA_EFFECT_CLOUD_RADIUS, entityMetadata.getValue()); - metadata.put(EntityData.BOUNDING_BOX_WIDTH, 2.0f * (float) entityMetadata.getValue()); - } else if (entityMetadata.getId() == 9) { - metadata.put(EntityData.EFFECT_COLOR, entityMetadata.getValue()); - } else if (entityMetadata.getId() == 11) { - Particle particle = (Particle) entityMetadata.getValue(); - int particleId = EffectUtils.getParticleId(session, particle.getType()); - if (particleId != -1) { - metadata.put(EntityData.AREA_EFFECT_CLOUD_PARTICLE_ID, particleId); - } + protected void initializeMetadata() { + super.initializeMetadata(); + // Without this the cloud doesn't appear, + dirtyMetadata.put(EntityData.AREA_EFFECT_CLOUD_DURATION, 600); + + // This disabled client side shrink of the cloud + dirtyMetadata.put(EntityData.AREA_EFFECT_CLOUD_RADIUS, 0.0f); + dirtyMetadata.put(EntityData.AREA_EFFECT_CLOUD_CHANGE_RATE, -0.005f); + dirtyMetadata.put(EntityData.AREA_EFFECT_CLOUD_CHANGE_ON_PICKUP, -0.5f); + + setFlag(EntityFlag.FIRE_IMMUNE, true); + } + + public void setRadius(EntityMetadata entityMetadata) { + float value = ((FloatEntityMetadata) entityMetadata).getPrimitiveValue(); + dirtyMetadata.put(EntityData.AREA_EFFECT_CLOUD_RADIUS, value); + dirtyMetadata.put(EntityData.BOUNDING_BOX_WIDTH, 2.0f * value); + } + + public void setParticle(EntityMetadata entityMetadata) { + Particle particle = entityMetadata.getValue(); + int particleId = EffectUtils.getParticleId(session, particle.getType()); + if (particleId != -1) { + dirtyMetadata.put(EntityData.AREA_EFFECT_CLOUD_PARTICLE_ID, particleId); } - super.updateBedrockMetadata(entityMetadata, session); } } diff --git a/connector/src/main/java/org/geysermc/connector/entity/BoatEntity.java b/connector/src/main/java/org/geysermc/connector/entity/BoatEntity.java index 025747940..7069f5cfb 100644 --- a/connector/src/main/java/org/geysermc/connector/entity/BoatEntity.java +++ b/connector/src/main/java/org/geysermc/connector/entity/BoatEntity.java @@ -26,13 +26,16 @@ package org.geysermc.connector.entity; import com.github.steveice10.mc.protocol.data.game.entity.metadata.EntityMetadata; +import com.github.steveice10.mc.protocol.data.game.entity.metadata.type.BooleanEntityMetadata; +import com.github.steveice10.mc.protocol.data.game.entity.metadata.type.IntEntityMetadata; import com.nukkitx.math.vector.Vector3f; import com.nukkitx.protocol.bedrock.data.entity.EntityData; import com.nukkitx.protocol.bedrock.packet.AnimatePacket; import com.nukkitx.protocol.bedrock.packet.MoveEntityAbsolutePacket; -import org.geysermc.connector.entity.type.EntityType; +import lombok.Getter; import org.geysermc.connector.network.session.GeyserSession; +import java.util.UUID; import java.util.concurrent.TimeUnit; public class BoatEntity extends Entity { @@ -52,28 +55,36 @@ public class BoatEntity extends Entity { private boolean isPaddlingRight; private float paddleTimeRight; + /** + * Saved for using the "pick" functionality on a boat. + */ + @Getter + private int variant; + // Looks too fast and too choppy with 0.1f, which is how I believe the Microsoftian client handles it private final float ROWING_SPEED = 0.05f; - public BoatEntity(long entityId, long geyserId, EntityType entityType, Vector3f position, Vector3f motion, Vector3f rotation) { - super(entityId, geyserId, entityType, position.add(0d, entityType.getOffset(), 0d), motion, rotation.add(90, 0, 90)); + public BoatEntity(GeyserSession session, long entityId, long geyserId, UUID uuid, EntityDefinition definition, Vector3f position, Vector3f motion, float yaw, float pitch, float headYaw) { + // Initial rotation is incorrect + super(session, entityId, geyserId, uuid, definition, position.add(0d, definition.offset(), 0d), motion, yaw + 90, 0, yaw + 90); // Required to be able to move on land 1.16.200+ or apply gravity not in the water 1.16.100+ - metadata.put(EntityData.IS_BUOYANT, (byte) 1); - metadata.put(EntityData.BUOYANCY_DATA, BUOYANCY_DATA); + dirtyMetadata.put(EntityData.IS_BUOYANT, (byte) 1); + dirtyMetadata.put(EntityData.BUOYANCY_DATA, BUOYANCY_DATA); } @Override - public void moveAbsolute(GeyserSession session, Vector3f position, Vector3f rotation, boolean isOnGround, boolean teleported) { + public void moveAbsolute(Vector3f position, float yaw, float pitch, float headYaw, boolean isOnGround, boolean teleported) { // We don't include the rotation (y) as it causes the boat to appear sideways - setPosition(position.add(0d, this.entityType.getOffset(), 0d)); - setRotation(Vector3f.from(rotation.getX() + 90, 0, rotation.getX() + 90)); + setPosition(position.add(0d, this.definition.offset(), 0d)); + this.yaw = yaw + 90; + this.headYaw = yaw + 90; setOnGround(isOnGround); MoveEntityAbsolutePacket moveEntityPacket = new MoveEntityAbsolutePacket(); moveEntityPacket.setRuntimeEntityId(geyserId); // Minimal glitching when ClientboundMoveVehiclePacket is sent - moveEntityPacket.setPosition(session.getRidingVehicleEntity() == this ? position.up(EntityType.PLAYER.getOffset() - this.entityType.getOffset()) : this.position); + moveEntityPacket.setPosition(session.getRidingVehicleEntity() == this ? position.up(EntityDefinitions.PLAYER.offset() - this.definition.offset()) : this.position); moveEntityPacket.setRotation(getBedrockRotation()); moveEntityPacket.setOnGround(isOnGround); moveEntityPacket.setTeleported(teleported); @@ -84,91 +95,62 @@ public class BoatEntity extends Entity { /** * Move the boat without making the adjustments needed to translate from Java */ - public void moveAbsoluteWithoutAdjustments(GeyserSession session, Vector3f position, Vector3f rotation, boolean isOnGround, boolean teleported) { - super.moveAbsolute(session, position, Vector3f.from(rotation.getX(), 0, rotation.getX()), isOnGround, teleported); + public void moveAbsoluteWithoutAdjustments(Vector3f position, float yaw, boolean isOnGround, boolean teleported) { + super.moveAbsolute(position, yaw, 0, yaw, isOnGround, teleported); } @Override - public void moveRelative(GeyserSession session, double relX, double relY, double relZ, Vector3f rotation, boolean isOnGround) { - super.moveRelative(session, relX, relY, relZ, Vector3f.from(rotation.getX(), 0, rotation.getX()), isOnGround); + public void moveRelative(double relX, double relY, double relZ, float yaw, float pitch, float headYaw, boolean isOnGround) { + super.moveRelative(relX, relY, relZ, yaw, 0, yaw, isOnGround); } @Override - public void updatePositionAndRotation(GeyserSession session, double moveX, double moveY, double moveZ, float yaw, float pitch, boolean isOnGround) { - moveRelative(session, moveX, moveY, moveZ, yaw + 90, pitch, isOnGround); + public void updatePositionAndRotation(double moveX, double moveY, double moveZ, float yaw, float pitch, boolean isOnGround) { + moveRelative(moveX, moveY, moveZ, yaw + 90, pitch, isOnGround); } @Override - public void updateRotation(GeyserSession session, float yaw, float pitch, boolean isOnGround) { - moveRelative(session, 0, 0, 0, Vector3f.from(yaw + 90, 0, 0), isOnGround); + public void updateRotation(float yaw, float pitch, boolean isOnGround) { + moveRelative(0, 0, 0, yaw + 90, 0, 0, isOnGround); } - @Override - public void updateBedrockMetadata(EntityMetadata entityMetadata, GeyserSession session) { - // Time since last hit - if (entityMetadata.getId() == 8) { - metadata.put(EntityData.HURT_TIME, entityMetadata.getValue()); - } + public void setVariant(EntityMetadata entityMetadata) { + variant = ((IntEntityMetadata) entityMetadata).getPrimitiveValue(); + dirtyMetadata.put(EntityData.VARIANT, variant); + } - // Rocking direction - if (entityMetadata.getId() == 9) { - metadata.put(EntityData.HURT_DIRECTION, entityMetadata.getValue()); - } - - // 'Health' in Bedrock, damage taken in Java - if (entityMetadata.getId() == 10) { - // Not exactly health but it makes motion in Bedrock - metadata.put(EntityData.HEALTH, 40 - ((int) (float) entityMetadata.getValue())); - } - - if (entityMetadata.getId() == 11) { - metadata.put(EntityData.VARIANT, entityMetadata.getValue()); - } else if (entityMetadata.getId() == 12) { - isPaddlingLeft = (boolean) entityMetadata.getValue(); - if (isPaddlingLeft) { - // Java sends simply "true" and "false" (is_paddling_left), Bedrock keeps sending packets as you're rowing - // This is an asynchronous method that emulates Bedrock rowing until "false" is sent. - paddleTimeLeft = 0f; - if (!this.passengers.isEmpty()) { - // Get the entity by the first stored passenger and convey motion in this manner - Entity entity = session.getEntityCache().getEntityByJavaId(this.passengers.iterator().nextLong()); - if (entity != null) { - updateLeftPaddle(session, entity); - } + public void setPaddlingLeft(EntityMetadata entityMetadata) { + isPaddlingLeft = ((BooleanEntityMetadata) entityMetadata).getPrimitiveValue(); + if (isPaddlingLeft) { + // Java sends simply "true" and "false" (is_paddling_left), Bedrock keeps sending packets as you're rowing + // This is an asynchronous method that emulates Bedrock rowing until "false" is sent. + paddleTimeLeft = 0f; + if (!this.passengers.isEmpty()) { + // Get the entity by the first stored passenger and convey motion in this manner + Entity entity = session.getEntityCache().getEntityByJavaId(this.passengers.iterator().nextLong()); + if (entity != null) { + updateLeftPaddle(session, entity); } - } else { - // Indicate that the row position should be reset - metadata.put(EntityData.ROW_TIME_LEFT, 0.0f); } + } else { + // Indicate that the row position should be reset + dirtyMetadata.put(EntityData.ROW_TIME_LEFT, 0.0f); } - else if (entityMetadata.getId() == 13) { - isPaddlingRight = (boolean) entityMetadata.getValue(); - if (isPaddlingRight) { - paddleTimeRight = 0f; - if (!this.passengers.isEmpty()) { - Entity entity = session.getEntityCache().getEntityByJavaId(this.passengers.iterator().nextLong()); - if (entity != null) { - updateRightPaddle(session, entity); - } - } - } else { - metadata.put(EntityData.ROW_TIME_RIGHT, 0.0f); - } - } else if (entityMetadata.getId() == 14) { - // Possibly - I don't think this does anything? - metadata.put(EntityData.BOAT_BUBBLE_TIME, entityMetadata.getValue()); - } - - super.updateBedrockMetadata(entityMetadata, session); } - @Override - public void updateBedrockMetadata(GeyserSession session) { - super.updateBedrockMetadata(session); - - // As these indicate to reset rowing, remove them until it is time to send them out again. - metadata.remove(EntityData.ROW_TIME_LEFT); - metadata.remove(EntityData.ROW_TIME_RIGHT); + public void setPaddlingRight(EntityMetadata entityMetadata) { + isPaddlingRight = ((BooleanEntityMetadata) entityMetadata).getPrimitiveValue(); + if (isPaddlingRight) { + paddleTimeRight = 0f; + if (!this.passengers.isEmpty()) { + Entity entity = session.getEntityCache().getEntityByJavaId(this.passengers.iterator().nextLong()); + if (entity != null) { + updateRightPaddle(session, entity); + } + } + } else { + dirtyMetadata.put(EntityData.ROW_TIME_RIGHT, 0.0f); + } } private void updateLeftPaddle(GeyserSession session, Entity rower) { diff --git a/connector/src/main/java/org/geysermc/connector/entity/CommandBlockMinecartEntity.java b/connector/src/main/java/org/geysermc/connector/entity/CommandBlockMinecartEntity.java index c04e9f0b7..fcd4e05a3 100644 --- a/connector/src/main/java/org/geysermc/connector/entity/CommandBlockMinecartEntity.java +++ b/connector/src/main/java/org/geysermc/connector/entity/CommandBlockMinecartEntity.java @@ -25,42 +25,33 @@ package org.geysermc.connector.entity; -import com.github.steveice10.mc.protocol.data.game.entity.metadata.EntityMetadata; import com.nukkitx.math.vector.Vector3f; import com.nukkitx.protocol.bedrock.data.entity.EntityData; -import net.kyori.adventure.text.Component; -import org.geysermc.connector.entity.type.EntityType; import org.geysermc.connector.network.session.GeyserSession; -import org.geysermc.connector.network.translators.chat.MessageTranslator; + +import java.util.UUID; public class CommandBlockMinecartEntity extends DefaultBlockMinecartEntity { - public CommandBlockMinecartEntity(long entityId, long geyserId, EntityType entityType, Vector3f position, Vector3f motion, Vector3f rotation) { - super(entityId, geyserId, entityType, position, motion, rotation); - // Required, or else the GUI will not open - metadata.put(EntityData.CONTAINER_TYPE, (byte) 16); - metadata.put(EntityData.CONTAINER_BASE_SIZE, 1); - // Required, or else the client does not bother to send a packet back with the new information - metadata.put(EntityData.COMMAND_BLOCK_ENABLED, (byte) 1); + public CommandBlockMinecartEntity(GeyserSession session, long 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 - public void updateBedrockMetadata(EntityMetadata entityMetadata, GeyserSession session) { - if (entityMetadata.getId() == 14) { - metadata.put(EntityData.COMMAND_BLOCK_COMMAND, entityMetadata.getValue()); - } - if (entityMetadata.getId() == 15) { - metadata.put(EntityData.COMMAND_BLOCK_LAST_OUTPUT, MessageTranslator.convertMessage((Component) entityMetadata.getValue())); - } - super.updateBedrockMetadata(entityMetadata, session); + protected void initializeMetadata() { + // Required, or else the GUI will not open + dirtyMetadata.put(EntityData.CONTAINER_TYPE, (byte) 16); + dirtyMetadata.put(EntityData.CONTAINER_BASE_SIZE, 1); + // Required, or else the client does not bother to send a packet back with the new information + dirtyMetadata.put(EntityData.COMMAND_BLOCK_ENABLED, (byte) 1); } /** * By default, the command block shown is purple on Bedrock, which does not match Java Edition's orange. */ @Override - public void updateDefaultBlockMetadata(GeyserSession session) { - metadata.put(EntityData.DISPLAY_ITEM, session.getBlockMappings().getCommandBlockRuntimeId()); - metadata.put(EntityData.DISPLAY_OFFSET, 6); + public void updateDefaultBlockMetadata() { + dirtyMetadata.put(EntityData.DISPLAY_ITEM, session.getBlockMappings().getCommandBlockRuntimeId()); + dirtyMetadata.put(EntityData.DISPLAY_OFFSET, 6); } } diff --git a/connector/src/main/java/org/geysermc/connector/entity/DefaultBlockMinecartEntity.java b/connector/src/main/java/org/geysermc/connector/entity/DefaultBlockMinecartEntity.java index 5df7ae440..f4991b312 100644 --- a/connector/src/main/java/org/geysermc/connector/entity/DefaultBlockMinecartEntity.java +++ b/connector/src/main/java/org/geysermc/connector/entity/DefaultBlockMinecartEntity.java @@ -26,11 +26,14 @@ package org.geysermc.connector.entity; import com.github.steveice10.mc.protocol.data.game.entity.metadata.EntityMetadata; +import com.github.steveice10.mc.protocol.data.game.entity.metadata.type.BooleanEntityMetadata; +import com.github.steveice10.mc.protocol.data.game.entity.metadata.type.IntEntityMetadata; import com.nukkitx.math.vector.Vector3f; import com.nukkitx.protocol.bedrock.data.entity.EntityData; -import org.geysermc.connector.entity.type.EntityType; import org.geysermc.connector.network.session.GeyserSession; +import java.util.UUID; + /** * This class is used as a base for minecarts with a default block to display like furnaces and spawners */ @@ -40,53 +43,48 @@ public class DefaultBlockMinecartEntity extends MinecartEntity { public int customBlockOffset = 0; public boolean showCustomBlock = false; - public DefaultBlockMinecartEntity(long entityId, long geyserId, EntityType entityType, Vector3f position, Vector3f motion, Vector3f rotation) { - super(entityId, geyserId, entityType, position, motion, rotation); + public DefaultBlockMinecartEntity(GeyserSession session, long 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); - metadata.put(EntityData.CUSTOM_DISPLAY, (byte) 1); + dirtyMetadata.put(EntityData.CUSTOM_DISPLAY, (byte) 1); } @Override - public void spawnEntity(GeyserSession session) { - updateDefaultBlockMetadata(session); - super.spawnEntity(session); + public void spawnEntity() { + updateDefaultBlockMetadata(); + super.spawnEntity(); } @Override - public void updateBedrockMetadata(EntityMetadata entityMetadata, GeyserSession session) { + public void setCustomBlock(EntityMetadata entityMetadata) { + customBlock = ((IntEntityMetadata) entityMetadata).getPrimitiveValue(); - // Custom block - if (entityMetadata.getId() == 11) { - customBlock = (int) entityMetadata.getValue(); - - if (showCustomBlock) { - metadata.put(EntityData.DISPLAY_ITEM, session.getBlockMappings().getBedrockBlockId(customBlock)); - } + if (showCustomBlock) { + dirtyMetadata.put(EntityData.DISPLAY_ITEM, session.getBlockMappings().getBedrockBlockId(customBlock)); } - - // Custom block offset - if (entityMetadata.getId() == 12) { - customBlockOffset = (int) entityMetadata.getValue(); - - if (showCustomBlock) { - metadata.put(EntityData.DISPLAY_OFFSET, customBlockOffset); - } - } - - // If the custom block should be enabled - if (entityMetadata.getId() == 13) { - if ((boolean) entityMetadata.getValue()) { - showCustomBlock = true; - metadata.put(EntityData.DISPLAY_ITEM, session.getBlockMappings().getBedrockBlockId(customBlock)); - metadata.put(EntityData.DISPLAY_OFFSET, customBlockOffset); - } else { - showCustomBlock = false; - updateDefaultBlockMetadata(session); - } - } - - super.updateBedrockMetadata(entityMetadata, session); } - public void updateDefaultBlockMetadata(GeyserSession session) { } + @Override + public void setCustomBlockOffset(EntityMetadata entityMetadata) { + customBlockOffset = ((IntEntityMetadata) entityMetadata).getPrimitiveValue(); + + if (showCustomBlock) { + dirtyMetadata.put(EntityData.DISPLAY_OFFSET, customBlockOffset); + } + } + + @Override + public void setShowCustomBlock(EntityMetadata entityMetadata) { + if (((BooleanEntityMetadata) entityMetadata).getPrimitiveValue()) { + showCustomBlock = true; + dirtyMetadata.put(EntityData.DISPLAY_ITEM, session.getBlockMappings().getBedrockBlockId(customBlock)); + dirtyMetadata.put(EntityData.DISPLAY_OFFSET, customBlockOffset); + } else { + showCustomBlock = false; + updateDefaultBlockMetadata(); + } + } + + public void updateDefaultBlockMetadata() { + } } diff --git a/connector/src/main/java/org/geysermc/connector/entity/EnderCrystalEntity.java b/connector/src/main/java/org/geysermc/connector/entity/EnderCrystalEntity.java index e4b60980f..a39c479f8 100644 --- a/connector/src/main/java/org/geysermc/connector/entity/EnderCrystalEntity.java +++ b/connector/src/main/java/org/geysermc/connector/entity/EnderCrystalEntity.java @@ -31,33 +31,31 @@ import com.nukkitx.math.vector.Vector3f; import com.nukkitx.math.vector.Vector3i; import com.nukkitx.protocol.bedrock.data.entity.EntityData; import com.nukkitx.protocol.bedrock.data.entity.EntityFlag; -import org.geysermc.connector.entity.type.EntityType; import org.geysermc.connector.network.session.GeyserSession; +import java.util.UUID; + public class EnderCrystalEntity extends Entity { - public EnderCrystalEntity(long entityId, long geyserId, EntityType entityType, Vector3f position, Vector3f motion, Vector3f rotation) { - super(entityId, geyserId, entityType, position, motion, rotation); - - // Bedrock 1.16.100+ - prevents the entity from appearing on fire itself when fire is underneath it - metadata.getFlags().setFlag(EntityFlag.FIRE_IMMUNE, true); + public EnderCrystalEntity(GeyserSession session, long 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 - public void updateBedrockMetadata(EntityMetadata entityMetadata, GeyserSession session) { + protected void initializeMetadata() { + super.initializeMetadata(); + // Bedrock 1.16.100+ - prevents the entity from appearing on fire itself when fire is underneath it + dirtyMetadata.getFlags().setFlag(EntityFlag.FIRE_IMMUNE, true); + } + + public void setBlockTarget(EntityMetadata entityMetadata) { // Show beam // Usually performed client-side on Bedrock except for Ender Dragon respawn event - if (entityMetadata.getId() == 8) { - if (entityMetadata.getValue() instanceof Position pos) { - metadata.put(EntityData.BLOCK_TARGET, Vector3i.from(pos.getX(), pos.getY(), pos.getZ())); - } else { - metadata.put(EntityData.BLOCK_TARGET, Vector3i.ZERO); - } + Position position = entityMetadata.getValue(); + if (position != null) { + dirtyMetadata.put(EntityData.BLOCK_TARGET, Vector3i.from(position.getX(), position.getY(), position.getZ())); + } else { + dirtyMetadata.put(EntityData.BLOCK_TARGET, Vector3i.ZERO); } - // There is a base located on the ender crystal - if (entityMetadata.getId() == 9) { - metadata.getFlags().setFlag(EntityFlag.SHOW_BOTTOM, (boolean) entityMetadata.getValue()); - } - super.updateBedrockMetadata(entityMetadata, session); } } diff --git a/connector/src/main/java/org/geysermc/connector/entity/Entity.java b/connector/src/main/java/org/geysermc/connector/entity/Entity.java index a965b35da..bdeacc9dc 100644 --- a/connector/src/main/java/org/geysermc/connector/entity/Entity.java +++ b/connector/src/main/java/org/geysermc/connector/entity/Entity.java @@ -26,8 +26,10 @@ package org.geysermc.connector.entity; import com.github.steveice10.mc.protocol.data.game.entity.metadata.EntityMetadata; -import com.github.steveice10.mc.protocol.data.game.entity.metadata.MetadataType; import com.github.steveice10.mc.protocol.data.game.entity.metadata.Pose; +import com.github.steveice10.mc.protocol.data.game.entity.metadata.type.BooleanEntityMetadata; +import com.github.steveice10.mc.protocol.data.game.entity.metadata.type.ByteEntityMetadata; +import com.github.steveice10.mc.protocol.data.game.entity.metadata.type.IntEntityMetadata; import com.nukkitx.math.vector.Vector3f; import com.nukkitx.protocol.bedrock.data.entity.EntityData; import com.nukkitx.protocol.bedrock.data.entity.EntityDataMap; @@ -38,19 +40,24 @@ 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.AccessLevel; import lombok.Getter; import lombok.Setter; import net.kyori.adventure.text.Component; -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.MathUtils; +import java.util.UUID; + @Getter @Setter public class Entity { + protected final GeyserSession session; + protected long entityId; protected final long geyserId; + protected UUID uuid; protected Vector3f position; protected Vector3f motion; @@ -58,85 +65,120 @@ public class Entity { /** * x = Yaw, y = Pitch, z = HeadYaw */ - protected Vector3f rotation; + protected float yaw; + protected float pitch; + protected float headYaw; /** * Saves if the entity should be on the ground. Otherwise entities like parrots are flapping when rotating */ protected boolean onGround; - protected EntityType entityType; + protected EntityDefinition definition; protected boolean valid; - protected LongOpenHashSet passengers = new LongOpenHashSet(); - protected EntityDataMap metadata = new EntityDataMap(); + /* Metadata about this specific entity */ + @Setter(AccessLevel.NONE) + protected float boundingBoxHeight; + @Setter(AccessLevel.NONE) + protected float boundingBoxWidth; + /* Metadata end */ + + protected LongOpenHashSet passengers = new LongOpenHashSet(); + /** + * A container to store temporary metadata before it's sent to Bedrock. + */ + protected final EntityDataMap dirtyMetadata = new EntityDataMap(); + /** + * The entity flags for the Bedrock entity. + * These must always be saved - if flags are updated and the other values aren't present, the Bedrock client will + * think they are set to false. + */ + @Getter(AccessLevel.NONE) + protected final EntityFlags flags = new EntityFlags(); + /** + * Indicates if flags have been updated and need to be sent to the client. + */ + @Getter(AccessLevel.NONE) + @Setter(AccessLevel.PROTECTED) // For players + private boolean flagsDirty = false; + + public Entity(GeyserSession session, long entityId, long geyserId, UUID uuid, EntityDefinition definition, Vector3f position, Vector3f motion, float yaw, float pitch, float headYaw) { + this.session = session; - public Entity(long entityId, long geyserId, EntityType entityType, Vector3f position, Vector3f motion, Vector3f rotation) { this.entityId = entityId; this.geyserId = geyserId; - this.entityType = entityType; + this.uuid = uuid; + this.definition = definition; this.motion = motion; - this.rotation = rotation; + this.yaw = yaw; + this.pitch = pitch; + this.headYaw = headYaw; this.valid = false; setPosition(position); setAir(getMaxAir()); - metadata.put(EntityData.SCALE, 1f); - metadata.put(EntityData.COLOR, 0); - metadata.put(EntityData.MAX_AIR_SUPPLY, getMaxAir()); - metadata.put(EntityData.LEASH_HOLDER_EID, -1L); - metadata.put(EntityData.BOUNDING_BOX_HEIGHT, entityType.getHeight()); - metadata.put(EntityData.BOUNDING_BOX_WIDTH, entityType.getWidth()); - EntityFlags flags = new EntityFlags(); - flags.setFlag(EntityFlag.HAS_GRAVITY, true); - flags.setFlag(EntityFlag.HAS_COLLISION, true); - flags.setFlag(EntityFlag.CAN_SHOW_NAME, true); - flags.setFlag(EntityFlag.CAN_CLIMB, true); - metadata.putFlags(flags); + initializeMetadata(); } - public void spawnEntity(GeyserSession session) { + /** + * Called on entity spawn. Used to populate the entity metadata and flags with default values. + */ + protected void initializeMetadata() { + dirtyMetadata.put(EntityData.SCALE, 1f); + dirtyMetadata.put(EntityData.COLOR, 0); + dirtyMetadata.put(EntityData.MAX_AIR_SUPPLY, getMaxAir()); + setDimensions(Pose.STANDING); + setFlag(EntityFlag.HAS_GRAVITY, true); + setFlag(EntityFlag.HAS_COLLISION, true); + setFlag(EntityFlag.CAN_SHOW_NAME, true); + setFlag(EntityFlag.CAN_CLIMB, true); + } + + public void spawnEntity() { AddEntityPacket addEntityPacket = new AddEntityPacket(); - addEntityPacket.setIdentifier(entityType.getIdentifier()); + addEntityPacket.setIdentifier(definition.identifier()); addEntityPacket.setRuntimeEntityId(geyserId); addEntityPacket.setUniqueEntityId(geyserId); addEntityPacket.setPosition(position); addEntityPacket.setMotion(motion); addEntityPacket.setRotation(getBedrockRotation()); - addEntityPacket.setEntityType(entityType.getType()); - addEntityPacket.getMetadata().putAll(metadata); + addEntityPacket.setEntityType(definition.bedrockId()); + addEntityPacket.getMetadata().putFlags(flags) + .putAll(dirtyMetadata); addAdditionalSpawnData(addEntityPacket); valid = true; session.sendUpstreamPacket(addEntityPacket); - session.getConnector().getLogger().debug("Spawned entity " + entityType + " at location " + position + " with id " + geyserId + " (java id " + entityId + ")"); + dirtyMetadata.clear(); + flagsDirty = false; + + session.getConnector().getLogger().debug("Spawned entity " + getClass().getName() + " 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 * - * @param session The GeyserSession * @return can be deleted */ - public boolean despawnEntity(GeyserSession session) { + public boolean despawnEntity() { if (!valid) return true; for (long passenger : passengers) { // Make sure all passengers on the despawned entity are updated Entity entity = session.getEntityCache().getEntityByJavaId(passenger); if (entity == null) continue; - entity.getMetadata().getOrCreateFlags().setFlag(EntityFlag.RIDING, false); - entity.updateBedrockMetadata(session); + entity.setFlag(EntityFlag.RIDING, false); + entity.updateBedrockMetadata(); } RemoveEntityPacket removeEntityPacket = new RemoveEntityPacket(); @@ -147,12 +189,14 @@ public class Entity { return true; } - public void moveRelative(GeyserSession session, double relX, double relY, double relZ, float yaw, float pitch, boolean isOnGround) { - moveRelative(session, relX, relY, relZ, Vector3f.from(yaw, pitch, this.rotation.getZ()), isOnGround); + public void moveRelative(double relX, double relY, double relZ, float yaw, float pitch, boolean isOnGround) { + moveRelative(relX, relY, relZ, yaw, pitch, this.headYaw, isOnGround); } - public void moveRelative(GeyserSession session, double relX, double relY, double relZ, Vector3f rotation, boolean isOnGround) { - setRotation(rotation); + public void moveRelative(double relX, double relY, double relZ, float yaw, float pitch, float headYaw, boolean isOnGround) { + setYaw(yaw); + setPitch(pitch); + setHeadYaw(headYaw); setOnGround(isOnGround); this.position = Vector3f.from(position.getX() + relX, position.getY() + relY, position.getZ() + relZ); @@ -166,13 +210,16 @@ public class Entity { session.sendUpstreamPacket(moveEntityPacket); } - public void moveAbsolute(GeyserSession session, Vector3f position, float yaw, float pitch, boolean isOnGround, boolean teleported) { - moveAbsolute(session, position, Vector3f.from(yaw, pitch, this.rotation.getZ()), isOnGround, teleported); + public void moveAbsolute(Vector3f position, float yaw, float pitch, boolean isOnGround, boolean teleported) { + moveAbsolute(position, yaw, pitch, this.headYaw, isOnGround, teleported); } - public void moveAbsolute(GeyserSession session, Vector3f position, Vector3f rotation, boolean isOnGround, boolean teleported) { + public void moveAbsolute(Vector3f position, float yaw, float pitch, float headYaw, boolean isOnGround, boolean teleported) { setPosition(position); - setRotation(rotation); + // Setters are intentional so it can be overridden in places like AbstractArrowEntity + setYaw(yaw); + setPitch(pitch); + setHeadYaw(headYaw); setOnGround(isOnGround); MoveEntityAbsolutePacket moveEntityPacket = new MoveEntityAbsolutePacket(); @@ -187,28 +234,25 @@ public class Entity { /** * Teleports an entity to a new location. Used in JavaTeleportEntityTranslator. - * @param session GeyserSession. * @param position The new position of the entity. * @param yaw The new yaw of the entity. * @param pitch The new pitch of the entity. * @param isOnGround Whether the entity is currently on the ground. */ - public void teleport(GeyserSession session, Vector3f position, float yaw, float pitch, boolean isOnGround) { - moveAbsolute(session, position, yaw, pitch, isOnGround, false); + public void teleport(Vector3f position, float yaw, float pitch, boolean isOnGround) { + moveAbsolute(position, yaw, pitch, isOnGround, false); } /** * Updates an entity's head position. Used in JavaRotateHeadTranslator. - * @param session GeyserSession. * @param headYaw The new head rotation of the entity. */ - public void updateHeadLookRotation(GeyserSession session, float headYaw) { - moveRelative(session, 0, 0, 0, Vector3f.from(headYaw, rotation.getY(), rotation.getZ()), onGround); + public void updateHeadLookRotation(float headYaw) { + moveRelative(0, 0, 0, headYaw, pitch, this.headYaw, onGround); } /** * Updates an entity's position and rotation. Used in JavaMoveEntityPosRotTranslator. - * @param session GeyserSession * @param moveX The new X offset of the current position. * @param moveY The new Y offset of the current position. * @param moveZ The new Z offset of the current position. @@ -216,106 +260,115 @@ public class Entity { * @param pitch The new pitch of the entity. * @param isOnGround Whether the entity is currently on the ground. */ - public void updatePositionAndRotation(GeyserSession session, double moveX, double moveY, double moveZ, float yaw, float pitch, boolean isOnGround) { - moveRelative(session, moveX, moveY, moveZ, Vector3f.from(rotation.getX(), pitch, yaw), isOnGround); + public void updatePositionAndRotation(double moveX, double moveY, double moveZ, float yaw, float pitch, boolean isOnGround) { + moveRelative(moveX, moveY, moveZ, this.yaw, pitch, yaw, isOnGround); } /** * Updates an entity's rotation. Used in JavaMoveEntityRotTranslator. - * @param session GeyserSession. * @param yaw The new yaw of the entity. * @param pitch The new pitch of the entity. * @param isOnGround Whether the entity is currently on the ground. */ - public void updateRotation(GeyserSession session, float yaw, float pitch, boolean isOnGround) { - updatePositionAndRotation(session, 0, 0, 0, yaw, pitch, isOnGround); + public void updateRotation(float yaw, float pitch, boolean isOnGround) { + updatePositionAndRotation(0, 0, 0, yaw, pitch, isOnGround); + } + + public final boolean getFlag(EntityFlag flag) { + return flags.getFlag(flag); } /** - * Applies the Java metadata to the local Bedrock metadata copy - * @param entityMetadata the Java entity metadata - * @param session GeyserSession + * Updates a flag value and determines if the flags would need synced with the Bedrock client. */ - public void updateBedrockMetadata(EntityMetadata entityMetadata, GeyserSession session) { - switch (entityMetadata.getId()) { - case 0: - if (entityMetadata.getType() == MetadataType.BYTE) { - byte xd = (byte) entityMetadata.getValue(); - metadata.getFlags().setFlag(EntityFlag.ON_FIRE, ((xd & 0x01) == 0x01) && !metadata.getFlags().getFlag(EntityFlag.FIRE_IMMUNE)); // Otherwise immune entities sometimes flicker onfire - metadata.getFlags().setFlag(EntityFlag.SNEAKING, (xd & 0x02) == 0x02); - metadata.getFlags().setFlag(EntityFlag.SPRINTING, (xd & 0x08) == 0x08); - // Swimming is ignored here and instead we rely on the pose - metadata.getFlags().setFlag(EntityFlag.GLIDING, (xd & 0x80) == 0x80); - - setInvisible(session, (xd & 0x20) == 0x20); - } - break; - case 1: // Air/bubbles - setAir((int) entityMetadata.getValue()); - break; - case 2: // custom name - setDisplayName(session, (Component) entityMetadata.getValue()); - break; - case 3: // is custom name visible - setDisplayNameVisible(entityMetadata); - break; - case 4: // silent - metadata.getFlags().setFlag(EntityFlag.SILENT, (boolean) entityMetadata.getValue()); - break; - case 5: // no gravity - metadata.getFlags().setFlag(EntityFlag.HAS_GRAVITY, !(boolean) entityMetadata.getValue()); - break; - case 6: // Pose change - typically used for bounding box and not animation - Pose pose = (Pose) entityMetadata.getValue(); - - metadata.getFlags().setFlag(EntityFlag.SLEEPING, pose.equals(Pose.SLEEPING)); - // Triggered when crawling - metadata.getFlags().setFlag(EntityFlag.SWIMMING, pose.equals(Pose.SWIMMING)); - setDimensions(pose); - break; - case 7: // Freezing ticks - // The value that Java edition gives us is in ticks, but Bedrock uses a float percentage of the strength 0.0 -> 1.0 - // The Java client caps its freezing tick percentage at 140 - int freezingTicks = Math.min((int) entityMetadata.getValue(), 140); - setFreezing(session, freezingTicks / 140f); - break; - } + public final void setFlag(EntityFlag flag, boolean value) { + flagsDirty |= flags.setFlag(flag, value); } /** * Sends the Bedrock metadata to the client - * @param session GeyserSession */ - public void updateBedrockMetadata(GeyserSession session) { - if (!valid) return; + public void updateBedrockMetadata() { + if (!valid) { + return; + } - SetEntityDataPacket entityDataPacket = new SetEntityDataPacket(); - entityDataPacket.setRuntimeEntityId(geyserId); - entityDataPacket.getMetadata().putAll(metadata); - session.sendUpstreamPacket(entityDataPacket); - } + if (!dirtyMetadata.isEmpty() || flagsDirty) { + SetEntityDataPacket entityDataPacket = new SetEntityDataPacket(); + entityDataPacket.setRuntimeEntityId(geyserId); + entityDataPacket.getMetadata().putFlags(flags); + entityDataPacket.getMetadata().putAll(dirtyMetadata); + session.sendUpstreamPacket(entityDataPacket); - /** - * If true, the entity should be shaking on the client's end. - * - * @return whether {@link EntityFlag#SHAKING} should be set to true. - */ - protected boolean isShaking(GeyserSession session) { - return false; - } - - protected void setDisplayName(GeyserSession session, Component name) { - if (name != null) { - String displayName = MessageTranslator.convertMessage(name, session.getLocale()); - metadata.put(EntityData.NAMETAG, displayName); - } else if (!metadata.getString(EntityData.NAMETAG).isEmpty()) { - // Clear nametag - metadata.put(EntityData.NAMETAG, ""); + dirtyMetadata.clear(); + flagsDirty = false; } } - protected void setDisplayNameVisible(EntityMetadata entityMetadata) { - metadata.put(EntityData.NAMETAG_ALWAYS_SHOW, (byte) ((boolean) entityMetadata.getValue() ? 1 : 0)); + public void setFlags(EntityMetadata entityMetadata) { + byte xd = ((ByteEntityMetadata) entityMetadata).getPrimitiveValue(); + setFlag(EntityFlag.ON_FIRE, ((xd & 0x01) == 0x01) && !getFlag(EntityFlag.FIRE_IMMUNE)); // Otherwise immune entities sometimes flicker onfire + setFlag(EntityFlag.SNEAKING, (xd & 0x02) == 0x02); + setFlag(EntityFlag.SPRINTING, (xd & 0x08) == 0x08); + // Swimming is ignored here and instead we rely on the pose + setFlag(EntityFlag.GLIDING, (xd & 0x80) == 0x80); + + setInvisible((xd & 0x20) == 0x20); + } + + /** + * Set a boolean - whether the entity is invisible or visible + * + * @param value true if the entity is invisible + */ + protected void setInvisible(boolean value) { + setFlag(EntityFlag.INVISIBLE, value); + } + + /** + * Set an int from 0 - this entity's maximum air - (air / maxAir) represents the percentage of bubbles left + */ + public final void setAir(EntityMetadata entityMetadata) { + setAir(((IntEntityMetadata) entityMetadata).getPrimitiveValue()); + } + + protected void setAir(int amount) { + dirtyMetadata.put(EntityData.AIR_SUPPLY, (short) MathUtils.constrain(amount, 0, getMaxAir())); + } + + protected int getMaxAir() { + return 300; + } + + public void setDisplayName(EntityMetadata entityMetadata) { + Component name = entityMetadata.getValue(); + if (name != null) { + String displayName = MessageTranslator.convertMessage(name, session.getLocale()); + dirtyMetadata.put(EntityData.NAMETAG, displayName); + } else if (!dirtyMetadata.getString(EntityData.NAMETAG).isEmpty()) { //TODO + // Clear nametag + dirtyMetadata.put(EntityData.NAMETAG, ""); + } + } + + public void setDisplayNameVisible(EntityMetadata entityMetadata) { + dirtyMetadata.put(EntityData.NAMETAG_ALWAYS_SHOW, (byte) (((BooleanEntityMetadata) entityMetadata).getPrimitiveValue() ? 1 : 0)); + } + + public void setGravity(EntityMetadata entityMetadata) { + setFlag(EntityFlag.HAS_GRAVITY, !((BooleanEntityMetadata) entityMetadata).getPrimitiveValue()); + } + + /** + * Usually used for bounding box and not animation. + */ + public void setPose(EntityMetadata entityMetadata) { + Pose pose = entityMetadata.getValue(); + + setFlag(EntityFlag.SLEEPING, pose.equals(Pose.SLEEPING)); + // Triggered when crawling + setFlag(EntityFlag.SWIMMING, pose.equals(Pose.SWIMMING)); + setDimensions(pose); } /** @@ -323,38 +376,33 @@ public class Entity { */ protected void setDimensions(Pose pose) { // No flexibility options for basic entities - //TODO don't even set this for basic entities since we already set it on entity initialization - metadata.put(EntityData.BOUNDING_BOX_WIDTH, entityType.getWidth()); - metadata.put(EntityData.BOUNDING_BOX_HEIGHT, entityType.getHeight()); + if (boundingBoxHeight != definition.height() || boundingBoxWidth != definition.width()) { + boundingBoxWidth = definition.width(); + boundingBoxHeight = definition.height(); + dirtyMetadata.put(EntityData.BOUNDING_BOX_WIDTH, boundingBoxWidth); + dirtyMetadata.put(EntityData.BOUNDING_BOX_HEIGHT, boundingBoxHeight); + } } /** * Set a float from 0-1 - how strong the "frozen" overlay should be on screen. */ - protected void setFreezing(GeyserSession session, float amount) { - metadata.put(EntityData.FREEZING_EFFECT_STRENGTH, amount); + public float setFreezing(EntityMetadata entityMetadata) { + // The value that Java edition gives us is in ticks, but Bedrock uses a float percentage of the strength 0.0 -> 1.0 + // The Java client caps its freezing tick percentage at 140 + int freezingTicks = Math.min(((IntEntityMetadata) entityMetadata).getPrimitiveValue(), 140); + float freezingPercentage = freezingTicks / 140f; + dirtyMetadata.put(EntityData.FREEZING_EFFECT_STRENGTH, freezingPercentage); + return freezingPercentage; } /** - * Set an int from 0 - this entity's maximum air - (air / maxAir) represents the percentage of bubbles left - * @param amount the amount of air - */ - protected void setAir(int amount) { - metadata.put(EntityData.AIR_SUPPLY, (short) MathUtils.constrain(amount, 0, getMaxAir())); - } - - protected int getMaxAir() { - return 300; - } - - /** - * Set a boolean - whether the entity is invisible or visible + * If true, the entity should be shaking on the client's end. * - * @param session the Geyser session - * @param value true if the entity is invisible + * @return whether {@link EntityFlag#SHAKING} should be set to true. */ - protected void setInvisible(GeyserSession session, boolean value) { - metadata.getFlags().setFlag(EntityFlag.INVISIBLE, value); + protected boolean isShaking() { + return false; } /** @@ -363,7 +411,7 @@ public class Entity { * @return the bedrock rotation */ public Vector3f getBedrockRotation() { - return Vector3f.from(rotation.getY(), rotation.getZ(), rotation.getX()); + return Vector3f.from(pitch, headYaw, yaw); } @SuppressWarnings("unchecked") diff --git a/connector/src/main/java/org/geysermc/connector/entity/EntityDefinition.java b/connector/src/main/java/org/geysermc/connector/entity/EntityDefinition.java new file mode 100644 index 000000000..c4100d95d --- /dev/null +++ b/connector/src/main/java/org/geysermc/connector/entity/EntityDefinition.java @@ -0,0 +1,140 @@ +/* + * 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; + +import com.github.steveice10.mc.protocol.data.game.entity.metadata.EntityMetadata; +import com.github.steveice10.mc.protocol.data.game.entity.metadata.MetadataType; +import com.github.steveice10.mc.protocol.data.game.entity.type.EntityType; +import it.unimi.dsi.fastutil.objects.ObjectArrayList; +import lombok.Setter; +import lombok.experimental.Accessors; +import org.geysermc.connector.entity.factory.BaseEntityFactory; +import org.geysermc.connector.entity.factory.EntityFactory; +import org.geysermc.connector.registry.Registries; + +import java.util.List; +import java.util.Locale; +import java.util.function.BiConsumer; + +/** + * Represents data for an entity. This includes properties such as height and width, as well as the list of entity + * metadata translators needed to translate the properties sent from the server. The translators are structured in such + * a way that inserting a new one (for example in version updates) is convenient. + * + * @param the entity type this definition represents + */ +public record EntityDefinition(EntityFactory factory, EntityType entityType, int bedrockId, String identifier, + float width, float height, float offset, List> translators) { + + public static Builder inherited(BaseEntityFactory factory, EntityDefinition parent) { + return inherited((EntityFactory) factory, parent); + } + + public static Builder inherited(EntityFactory factory, EntityDefinition parent) { + return new Builder<>(factory, parent.entityType, parent.bedrockId, parent.identifier, parent.width, parent.height, parent.offset, new ObjectArrayList<>(parent.translators)); + } + + public static Builder builder(EntityFactory factory) { + return new Builder<>(factory); + } + + @Setter + @Accessors(fluent = true, chain = true) + public static class Builder { + private final EntityFactory factory; + private EntityType type; + private int bedrockId; + private String identifier; + private float width; + private float height; + private float offset; + private final List> translators; + + private Builder(EntityFactory factory) { + this.factory = factory; + translators = new ObjectArrayList<>(); + } + + public Builder(EntityFactory factory, EntityType type, int bedrockId, String identifier, float width, float height, float offset, List> translators) { + this.factory = factory; + this.type = type; + this.bedrockId = bedrockId; + this.identifier = identifier; + this.width = width; + this.height = height; + this.offset = offset; + this.translators = translators; + } + + /** + * Sets the height and width as one value + */ + public Builder heightAndWidth(float value) { + height = value; + width = value; + return this; + } + + /** + * Resets the identifier as well + */ + public Builder type(EntityType type) { + this.type = type; + identifier = null; + return this; + } + + public Builder addTranslator(MetadataType type, BiConsumer> translateFunction) { + translators.add(new EntityMetadataTranslator<>(type, translateFunction)); + return this; + } + + public Builder addTranslator(EntityMetadataTranslator translator) { + translators.add(translator); + return this; + } + + public EntityDefinition build() { + return build(true); + } + + /** + * @param register whether to register this entity in the Registries for entity types. Generally this should be + * set to false if we're not expecting this entity to spawn from the network. + */ + public EntityDefinition build(boolean register) { + if (identifier == null && type != null) { + identifier = "minecraft:" + type.name().toLowerCase(Locale.ROOT); + } + EntityDefinition definition = new EntityDefinition<>(factory, type, bedrockId, identifier, width, height, offset, translators); + if (register && definition.entityType() != null) { + Registries.ENTITY_DEFINITIONS.get().putIfAbsent(definition.entityType(), definition); + Registries.JAVA_ENTITY_IDENTIFIERS.get().putIfAbsent(definition.identifier(), definition); + } + return definition; + } + } +} diff --git a/connector/src/main/java/org/geysermc/connector/entity/EntityDefinitions.java b/connector/src/main/java/org/geysermc/connector/entity/EntityDefinitions.java new file mode 100644 index 000000000..926c86c29 --- /dev/null +++ b/connector/src/main/java/org/geysermc/connector/entity/EntityDefinitions.java @@ -0,0 +1,999 @@ +/* + * 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; + +import com.github.steveice10.mc.protocol.data.game.entity.metadata.MetadataType; +import com.github.steveice10.mc.protocol.data.game.entity.metadata.type.BooleanEntityMetadata; +import com.github.steveice10.mc.protocol.data.game.entity.metadata.type.FloatEntityMetadata; +import com.github.steveice10.mc.protocol.data.game.entity.type.EntityType; +import com.nukkitx.protocol.bedrock.data.entity.EntityData; +import com.nukkitx.protocol.bedrock.data.entity.EntityFlag; +import net.kyori.adventure.text.Component; +import org.geysermc.connector.entity.factory.BaseEntityFactory; +import org.geysermc.connector.entity.factory.EntityFactory; +import org.geysermc.connector.entity.factory.ExperienceOrbEntityFactory; +import org.geysermc.connector.entity.factory.PaintingEntityFactory; +import org.geysermc.connector.entity.living.*; +import org.geysermc.connector.entity.living.animal.*; +import org.geysermc.connector.entity.living.animal.horse.*; +import org.geysermc.connector.entity.living.animal.tameable.CatEntity; +import org.geysermc.connector.entity.living.animal.tameable.ParrotEntity; +import org.geysermc.connector.entity.living.animal.tameable.TameableEntity; +import org.geysermc.connector.entity.living.animal.tameable.WolfEntity; +import org.geysermc.connector.entity.living.merchant.AbstractMerchantEntity; +import org.geysermc.connector.entity.living.merchant.VillagerEntity; +import org.geysermc.connector.entity.living.monster.*; +import org.geysermc.connector.entity.living.monster.raid.PillagerEntity; +import org.geysermc.connector.entity.living.monster.raid.RaidParticipantEntity; +import org.geysermc.connector.entity.living.monster.raid.SpellcasterIllagerEntity; +import org.geysermc.connector.entity.living.monster.raid.VindicatorEntity; +import org.geysermc.connector.entity.player.PlayerEntity; +import org.geysermc.connector.network.translators.chat.MessageTranslator; + +public final class EntityDefinitions { + public static final EntityDefinition AREA_EFFECT_CLOUD; + public static final EntityDefinition ARMOR_STAND; + public static final EntityDefinition ARROW; + public static final EntityDefinition AXOLOTL; + public static final EntityDefinition BAT; + public static final EntityDefinition BEE; + public static final EntityDefinition BLAZE; + public static final EntityDefinition BOAT; + public static final EntityDefinition CAT; + public static final EntityDefinition CAVE_SPIDER; + public static final EntityDefinition CHICKEN; + public static final EntityDefinition CREEPER; + public static final EntityDefinition COD; + public static final EntityDefinition COW; + public static final EntityDefinition DONKEY; + public static final EntityDefinition DOLPHIN; + public static final EntityDefinition DRAGON_FIREBALL; + public static final EntityDefinition DROWNED; + public static final EntityDefinition ELDER_GUARDIAN; + public static final EntityDefinition ENDERMAN; + public static final EntityDefinition ENDERMITE; + public static final EntityDefinition ENDER_DRAGON; + public static final EntityDefinition END_CRYSTAL; + public static final EntityDefinition EVOKER; + public static final EntityDefinition EVOKER_FANGS; + public static final EntityDefinition EXPERIENCE_ORB; + public static final EntityDefinition EYE_OF_ENDER; + public static final EntityDefinition FALLING_BLOCK; + public static final EntityDefinition FIREBALL; + public static final EntityDefinition FISHING_BOBBER; + public static final EntityDefinition FIREWORK_ROCKET; + public static final EntityDefinition FOX; + public static final EntityDefinition GIANT; + public static final EntityDefinition GHAST; + public static final EntityDefinition GLOW_ITEM_FRAME; + public static final EntityDefinition GLOW_SQUID; + public static final EntityDefinition GOAT; + public static final EntityDefinition GUARDIAN; + public static final EntityDefinition HOGLIN; + public static final EntityDefinition HORSE; + public static final EntityDefinition HUSK; + public static final EntityDefinition ILLUSIONER; // Not present on Bedrock + public static final EntityDefinition IRON_GOLEM; + public static final EntityDefinition ITEM; + public static final EntityDefinition ITEM_FRAME; + public static final EntityDefinition LEASH_KNOT; + public static final EntityDefinition LIGHTNING_BOLT; + public static final EntityDefinition LLAMA; + public static final EntityDefinition LLAMA_SPIT; + public static final EntityDefinition MAGMA_CUBE; + public static final EntityDefinition MINECART; + public static final EntityDefinition MINECART_CHEST; + public static final EntityDefinition MINECART_COMMAND_BLOCK; + public static final EntityDefinition MINECART_HOPPER; + public static final EntityDefinition MINECART_FURNACE; // Not present on Bedrock + public static final EntityDefinition MINECART_SPAWNER; // Not present on Bedrock + public static final EntityDefinition MINECART_TNT; + public static final EntityDefinition MOOSHROOM; + public static final EntityDefinition MULE; + public static final EntityDefinition OCELOT; + public static final EntityDefinition PAINTING; + public static final EntityDefinition PANDA; + public static final EntityDefinition PARROT; + public static final EntityDefinition PHANTOM; + public static final EntityDefinition PIG; + public static final EntityDefinition PIGLIN; + public static final EntityDefinition PIGLIN_BRUTE; + public static final EntityDefinition PILLAGER; + public static final EntityDefinition PLAYER; + public static final EntityDefinition POLAR_BEAR; + public static final EntityDefinition PRIMED_TNT; + public static final EntityDefinition PUFFERFISH; + public static final EntityDefinition RABBIT; + public static final EntityDefinition RAVAGER; + public static final EntityDefinition SALMON; + public static final EntityDefinition SHEEP; + public static final EntityDefinition SHULKER; + public static final EntityDefinition SHULKER_BULLET; + public static final EntityDefinition SILVERFISH; + public static final EntityDefinition SKELETON; + public static final EntityDefinition SKELETON_HORSE; + public static final EntityDefinition SLIME; + public static final EntityDefinition SMALL_FIREBALL; + public static final EntityDefinition SNOWBALL; + public static final EntityDefinition SNOW_GOLEM; + public static final EntityDefinition SPECTRAL_ARROW; + public static final EntityDefinition SPIDER; + public static final EntityDefinition SQUID; + public static final EntityDefinition STRAY; + public static final EntityDefinition STRIDER; + public static final EntityDefinition THROWN_EGG; + public static final EntityDefinition THROWN_ENDERPEARL; + public static final EntityDefinition THROWN_EXP_BOTTLE; + public static final EntityDefinition THROWN_POTION; + public static final EntityDefinition TROPICAL_FISH; + public static final EntityDefinition TURTLE; + public static final EntityDefinition TRADER_LLAMA; + public static final EntityDefinition TRIDENT; + public static final EntityDefinition WANDERING_TRADER; + public static final EntityDefinition WITCH; + public static final EntityDefinition WITHER; + public static final EntityDefinition WITHER_SKELETON; + public static final EntityDefinition WITHER_SKULL; + public static final EntityDefinition WOLF; + public static final EntityDefinition VILLAGER; + public static final EntityDefinition VINDICATOR; + public static final EntityDefinition VEX; + public static final EntityDefinition ZOGLIN; + public static final EntityDefinition ZOMBIE; + public static final EntityDefinition ZOMBIE_HORSE; + public static final EntityDefinition ZOMBIE_VILLAGER; + public static final EntityDefinition ZOMBIFIED_PIGLIN; + + /** + * Is not sent over the network + */ + public static final EntityDefinition ENDER_DRAGON_PART; + /** + * Special Bedrock type + */ + public static final EntityDefinition WITHER_SKULL_DANGEROUS; + + static { + EntityDefinition entityBase = EntityDefinition.builder((BaseEntityFactory) Entity::new) + .addTranslator(MetadataType.BYTE, Entity::setFlags) + .addTranslator(MetadataType.INT, Entity::setAir) // Air/bubbles + .addTranslator(MetadataType.OPTIONAL_CHAT, Entity::setDisplayName) + .addTranslator(MetadataType.BOOLEAN, Entity::setDisplayNameVisible) + .addTranslator(MetadataType.BOOLEAN, (entity, entityMetadata) -> entity.setFlag(EntityFlag.SILENT, ((BooleanEntityMetadata) entityMetadata).getPrimitiveValue())) + .addTranslator(MetadataType.BOOLEAN, Entity::setGravity) + .addTranslator(MetadataType.POSE, Entity::setPose) + .addTranslator(MetadataType.INT, Entity::setFreezing) + .build(); + + // Extends entity + { + AREA_EFFECT_CLOUD = EntityDefinition.inherited(AreaEffectCloudEntity::new, entityBase) + .type(EntityType.AREA_EFFECT_CLOUD) + .bedrockId(95) + .height(0.5f).width(1.0f) + .addTranslator(MetadataType.FLOAT, AreaEffectCloudEntity::setRadius) + .addTranslator(MetadataType.INT, (entity, entityMetadata) -> entity.getDirtyMetadata().put(EntityData.EFFECT_COLOR, entityMetadata.getValue())) + .addTranslator(null) // Waiting + .addTranslator(MetadataType.PARTICLE, AreaEffectCloudEntity::setParticle) + .build(); + BOAT = EntityDefinition.inherited(BoatEntity::new, entityBase) + .type(EntityType.BOAT) + .bedrockId(90) + .height(0.6f).width(1.6f) + .offset(0.35f) + .addTranslator(MetadataType.INT, (boatEntity, entityMetadata) -> boatEntity.getDirtyMetadata().put(EntityData.HURT_TIME, entityMetadata.getValue())) // Time since last hit + .addTranslator(MetadataType.INT, (boatEntity, entityMetadata) -> boatEntity.getDirtyMetadata().put(EntityData.HURT_DIRECTION, entityMetadata.getValue())) // Rocking direction + .addTranslator(MetadataType.FLOAT, (boatEntity, entityMetadata) -> + // 'Health' in Bedrock, damage taken in Java - it makes motion in Bedrock + boatEntity.getDirtyMetadata().put(EntityData.HEALTH, 40 - ((int) ((FloatEntityMetadata) entityMetadata).getPrimitiveValue()))) + .addTranslator(MetadataType.INT, BoatEntity::setVariant) + .addTranslator(MetadataType.BOOLEAN, BoatEntity::setPaddlingLeft) + .addTranslator(MetadataType.BOOLEAN, BoatEntity::setPaddlingRight) + .addTranslator(MetadataType.INT, (boatEntity, entityMetadata) -> boatEntity.getDirtyMetadata().put(EntityData.BOAT_BUBBLE_TIME, entityMetadata.getValue())) // May not actually do anything + .build(); + DRAGON_FIREBALL = EntityDefinition.inherited(ItemedFireballEntity::new, entityBase) + .type(EntityType.DRAGON_FIREBALL) + .bedrockId(79) + .heightAndWidth(1.0f) + .build(); + END_CRYSTAL = EntityDefinition.inherited(EnderCrystalEntity::new, entityBase) + .type(EntityType.END_CRYSTAL) + .bedrockId(71) + .heightAndWidth(2.0f) + .addTranslator(MetadataType.OPTIONAL_POSITION, EnderCrystalEntity::setBlockTarget) + .addTranslator(MetadataType.BOOLEAN, + (enderCrystalEntity, entityMetadata) -> enderCrystalEntity.setFlag(EntityFlag.SHOW_BOTTOM, ((BooleanEntityMetadata) entityMetadata).getPrimitiveValue())) // There is a base located on the ender crystal + .build(); + EXPERIENCE_ORB = EntityDefinition.inherited((ExperienceOrbEntityFactory) ExpOrbEntity::new, entityBase) + .type(EntityType.EXPERIENCE_ORB) + .bedrockId(69) + .identifier("minecraft:xp_orb") + .build(); + EVOKER_FANGS = EntityDefinition.inherited(entityBase.factory(), entityBase) + .type(EntityType.EVOKER_FANGS) + .bedrockId(103) + .height(0.8f).width(0.5f) + .identifier("minecraft:evocation_fang") + .build(); + EYE_OF_ENDER = EntityDefinition.inherited(Entity::new, entityBase) + .type(EntityType.EYE_OF_ENDER) + .bedrockId(70) + .heightAndWidth(0.25f) + .identifier("minecraft:eye_of_ender_signal") + .build(); + FALLING_BLOCK = EntityDefinition.inherited(new EntityFactory() { + }, entityBase) // TODO + .type(EntityType.FALLING_BLOCK) + .bedrockId(66) + .heightAndWidth(0.98f) + .build(); + FIREBALL = EntityDefinition.inherited(ItemedFireballEntity::new, entityBase) + .type(EntityType.FIREBALL) + .bedrockId(85) + .heightAndWidth(1.0f) + .build(); + FIREWORK_ROCKET = EntityDefinition.inherited(FireworkEntity::new, entityBase) + .type(EntityType.FIREWORK_ROCKET) + .bedrockId(72) + .heightAndWidth(0.25f) + .identifier("minecraft:fireworks_rocket") + .addTranslator(MetadataType.ITEM, FireworkEntity::setFireworkItem) + .addTranslator(MetadataType.OPTIONAL_VARINT, FireworkEntity::setPlayerGliding) + .build(); + FISHING_BOBBER = EntityDefinition.inherited(new EntityFactory() { + }, entityBase) //TODO + .type(EntityType.FISHING_BOBBER) + .bedrockId(77) + .identifier("minecraft:fishing_book") + .addTranslator(MetadataType.INT, FishingHookEntity::setHookedEntity) + .build(); + ITEM = EntityDefinition.inherited(ItemEntity::new, entityBase) + .type(EntityType.ITEM) + .bedrockId(64) + .heightAndWidth(0.25f) + .offset(0.125f) + .addTranslator(MetadataType.ITEM, ItemEntity::setItem) + .build(); + LEASH_KNOT = EntityDefinition.inherited(LeashKnotEntity::new, entityBase) + .type(EntityType.LEASH_KNOT) + .bedrockId(88) + .height(0.5f).width(0.375f) + .build(); + LIGHTNING_BOLT = EntityDefinition.inherited(LightningEntity::new, entityBase) + .type(EntityType.LIGHTNING_BOLT) + .bedrockId(93) + .build(); + LLAMA_SPIT = EntityDefinition.inherited(ThrowableEntity::new, entityBase) + .type(EntityType.LLAMA_SPIT) + .bedrockId(102) + .heightAndWidth(0.25f) + .build(); + PAINTING = EntityDefinition.inherited((PaintingEntityFactory) PaintingEntity::new, entityBase) + .type(EntityType.PAINTING) + .bedrockId(83) + .build(); + PRIMED_TNT = EntityDefinition.inherited(TNTEntity::new, entityBase) + .type(EntityType.PRIMED_TNT) + .bedrockId(65) + .heightAndWidth(0.98f) + .identifier("minecraft:tnt") + .addTranslator(MetadataType.INT, TNTEntity::setFuseLength) + .build(); + SHULKER_BULLET = EntityDefinition.inherited(ThrowableEntity::new, entityBase) + .type(EntityType.SHULKER_BULLET) + .bedrockId(76) + .heightAndWidth(0.3125f) + .build(); + SMALL_FIREBALL = EntityDefinition.inherited(ItemedFireballEntity::new, entityBase) + .type(EntityType.SMALL_FIREBALL) + .bedrockId(94) + .heightAndWidth(0.3125f) + .build(); + SNOWBALL = EntityDefinition.inherited(ThrowableItemEntity::new, entityBase) + .type(EntityType.SNOWBALL) + .bedrockId(81) + .heightAndWidth(0.25f) + .build(); + THROWN_ENDERPEARL = EntityDefinition.inherited(ThrowableItemEntity::new, entityBase) + .type(EntityType.THROWN_ENDERPEARL) + .bedrockId(87) + .heightAndWidth(0.25f) + .identifier("minecraft:ender_pearl") + .build(); + THROWN_EGG = EntityDefinition.inherited(ThrowableItemEntity::new, entityBase) + .type(EntityType.THROWN_EGG) + .bedrockId(82) + .heightAndWidth(0.25f) + .identifier("minecraft:egg") + .build(); + THROWN_EXP_BOTTLE = EntityDefinition.inherited(ThrowableItemEntity::new, entityBase) + .type(EntityType.THROWN_EXP_BOTTLE) + .bedrockId(68) + .heightAndWidth(0.25f) + .identifier("minecraft:xp_bottle") + .build(); + THROWN_POTION = EntityDefinition.inherited(ThrownPotionEntity::new, entityBase) + .type(EntityType.THROWN_POTION) + .bedrockId(86) + .heightAndWidth(0.25f) + .identifier("minecraft:splash_potion") + .addTranslator(MetadataType.ITEM, ThrownPotionEntity::setPotion) + .build(); + + EntityDefinition abstractArrowBase = EntityDefinition.inherited(AbstractArrowEntity::new, entityBase) + .addTranslator(MetadataType.BYTE, AbstractArrowEntity::setArrowFlags) + .addTranslator(null) // "Piercing level" + .build(); + ARROW = EntityDefinition.inherited(TippedArrowEntity::new, abstractArrowBase) + .type(EntityType.ARROW) + .bedrockId(80) + .heightAndWidth(0.25f) + .addTranslator(MetadataType.INT, TippedArrowEntity::setPotionEffectColor) + .build(); + SPECTRAL_ARROW = EntityDefinition.inherited(abstractArrowBase.factory(), abstractArrowBase) + .type(EntityType.SPECTRAL_ARROW) + .bedrockId(80) + .heightAndWidth(0.25f) + .identifier("minecraft:arrow") + .build(); + TRIDENT = EntityDefinition.inherited(TridentEntity::new, abstractArrowBase) // TODO remove class + .type(EntityType.TRIDENT) + .bedrockId(73) + .identifier("minecraft:thrown_trident") + .addTranslator(null) // Loyalty + .addTranslator(MetadataType.BOOLEAN, (tridentEntity, entityMetadata) -> tridentEntity.setFlag(EntityFlag.ENCHANTED, ((BooleanEntityMetadata) entityMetadata).getPrimitiveValue())) + .build(); + + // Item frames are handled differently as they are blocks, not items, in Bedrock + ITEM_FRAME = EntityDefinition.inherited(new EntityFactory() { + }, entityBase) // TODO + .type(EntityType.ITEM_FRAME) + .addTranslator(MetadataType.ITEM, ItemFrameEntity::setItemInFrame) + .addTranslator(MetadataType.ITEM, ItemFrameEntity::setItemRotation) + .build(); + GLOW_ITEM_FRAME = EntityDefinition.inherited(ITEM_FRAME.factory(), ITEM_FRAME) + .type(EntityType.GLOW_ITEM_FRAME) + .build(); + + MINECART = EntityDefinition.inherited(MinecartEntity::new, entityBase) + .type(EntityType.MINECART) + .bedrockId(84) + .height(0.7f).width(0.98f) + .offset(0.35f) + .addTranslator(MetadataType.INT, (minecartEntity, entityMetadata) -> minecartEntity.getDirtyMetadata().put(EntityData.HEALTH, entityMetadata.getValue())) + .addTranslator(MetadataType.INT, (minecartEntity, entityMetadata) -> minecartEntity.getDirtyMetadata().put(EntityData.HURT_DIRECTION, entityMetadata.getValue())) // Direction in which the minecart is shaking + .addTranslator(MetadataType.FLOAT, (minecartEntity, entityMetadata) -> + // Power in Java, time in Bedrock + minecartEntity.getDirtyMetadata().put(EntityData.HURT_TIME, Math.min((int) ((FloatEntityMetadata) entityMetadata).getPrimitiveValue(), 15))) + .addTranslator(MetadataType.BLOCK_STATE, MinecartEntity::setCustomBlock) + .addTranslator(MetadataType.INT, MinecartEntity::setCustomBlockOffset) + .addTranslator(MetadataType.BOOLEAN, MinecartEntity::setShowCustomBlock) + .build(); + MINECART_CHEST = EntityDefinition.inherited(MINECART.factory(), MINECART) + .type(EntityType.MINECART_CHEST) + .identifier("minecraft:chest_minecart") + .build(); + MINECART_COMMAND_BLOCK = EntityDefinition.inherited(CommandBlockMinecartEntity::new, MINECART) + .type(EntityType.MINECART_COMMAND_BLOCK) + .bedrockId(100) + .identifier("minecraft:command_block_minecart") + .addTranslator(MetadataType.STRING, (entity, entityMetadata) -> entity.getDirtyMetadata().put(EntityData.COMMAND_BLOCK_COMMAND, entityMetadata.getValue())) + .addTranslator(MetadataType.CHAT, (entity, entityMetadata) -> entity.getDirtyMetadata().put(EntityData.COMMAND_BLOCK_LAST_OUTPUT, MessageTranslator.convertMessage(entityMetadata.getValue()))) + .build(); + MINECART_FURNACE = EntityDefinition.inherited(FurnaceMinecartEntity::new, MINECART) + .type(EntityType.MINECART_FURNACE) + .identifier("minecraft:minecart") + .addTranslator(MetadataType.BOOLEAN, FurnaceMinecartEntity::setHasFuel) + .build(); + MINECART_HOPPER = EntityDefinition.inherited(MINECART.factory(), MINECART) + .type(EntityType.MINECART_HOPPER) + .identifier("minecraft:hopper_minecart") + .build(); + MINECART_SPAWNER = EntityDefinition.inherited(SpawnerMinecartEntity::new, MINECART) + .type(EntityType.MINECART_SPAWNER) + .identifier("minecraft:minecart") + .build(); + MINECART_TNT = EntityDefinition.inherited(MINECART.factory(), MINECART) + .type(EntityType.MINECART_TNT) + .identifier("minecraft:tnt_minecart") + .build(); + + WITHER_SKULL = EntityDefinition.inherited(WitherSkullEntity::new, entityBase) + .type(EntityType.WITHER_SKULL) + .bedrockId(89) + .heightAndWidth(0.3125f) + .addTranslator(MetadataType.BOOLEAN, WitherSkullEntity::setDangerous) + .build(); + WITHER_SKULL_DANGEROUS = EntityDefinition.inherited(WITHER_SKULL.factory(), WITHER_SKULL) + .bedrockId(91) + .build(false); + } + + EntityDefinition livingEntityBase = EntityDefinition.inherited(LivingEntity::new, entityBase) + .addTranslator(MetadataType.BYTE, LivingEntity::setLivingEntityFlags) + .addTranslator(MetadataType.FLOAT, LivingEntity::setHealth) + .addTranslator(MetadataType.FLOAT, + (livingEntity, entityMetadata) -> livingEntity.getDirtyMetadata().put(EntityData.EFFECT_COLOR, entityMetadata.getValue())) + .addTranslator(MetadataType.BOOLEAN, + (livingEntity, entityMetadata) -> livingEntity.getDirtyMetadata().put(EntityData.EFFECT_AMBIENT, (byte) (((BooleanEntityMetadata) entityMetadata).getPrimitiveValue() ? 1 : 0))) + .addTranslator(null) // Arrow count + .addTranslator(null) // Stinger count + .addTranslator(MetadataType.POSITION, LivingEntity::setBedPosition) + .build(); + + ARMOR_STAND = EntityDefinition.inherited(ArmorStandEntity::new, livingEntityBase) + .type(EntityType.ARMOR_STAND) + .bedrockId(61) + .height(1.975f).width(0.5f) + .addTranslator(MetadataType.BYTE, ArmorStandEntity::setArmorStandFlags) + .addTranslator(MetadataType.ROTATION, ArmorStandEntity::setHeadRotation) + .addTranslator(MetadataType.ROTATION, ArmorStandEntity::setBodyRotation) + .addTranslator(MetadataType.ROTATION, ArmorStandEntity::setLeftArmRotation) + .addTranslator(MetadataType.ROTATION, ArmorStandEntity::setRightArmRotation) + .addTranslator(MetadataType.ROTATION, ArmorStandEntity::setLeftLegRotation) + .addTranslator(MetadataType.ROTATION, ArmorStandEntity::setRightLegRotation) + .build(); + PLAYER = EntityDefinition.inherited(null, livingEntityBase) + .type(EntityType.PLAYER) + .bedrockId(63) + .height(1.8f).width(0.6f) + .offset(1.62f) + .addTranslator(MetadataType.FLOAT, PlayerEntity::setAbsorptionHearts) + .addTranslator(null) // Player score + .addTranslator(MetadataType.BYTE, PlayerEntity::setSkinVisibility) + .addTranslator(null) // Player main hand + .addTranslator(MetadataType.NBT_TAG, PlayerEntity::setLeftParrot) + .addTranslator(MetadataType.NBT_TAG, PlayerEntity::setRightParrot) + .build(); + + EntityDefinition mobEntityBase = EntityDefinition.inherited(MobEntity::new, livingEntityBase) + .addTranslator(MetadataType.BYTE, MobEntity::setMobFlags) + .build(); + + // Extends mob + { + BAT = EntityDefinition.inherited(BatEntity::new, mobEntityBase) + .type(EntityType.BAT) + .bedrockId(19) + .height(0.9f).width(0.5f) + .addTranslator(MetadataType.BYTE, BatEntity::setBatFlags) + .build(); + BLAZE = EntityDefinition.inherited(BlazeEntity::new, mobEntityBase) + .type(EntityType.BLAZE) + .bedrockId(43) + .height(1.8f).width(0.6f) + .addTranslator(MetadataType.BYTE, BlazeEntity::setBlazeFlags) + .build(); + CAVE_SPIDER = EntityDefinition.inherited(MonsterEntity::new, mobEntityBase) + .type(EntityType.CAVE_SPIDER) + .bedrockId(40) + .height(0.5f).width(0.7f) + .build(); + CREEPER = EntityDefinition.inherited(CreeperEntity::new, mobEntityBase) + .type(EntityType.CREEPER) + .bedrockId(33) + .height(1.7f).width(0.6f) + .offset(1.62f) + .addTranslator(MetadataType.INT, CreeperEntity::setSwelling) + .addTranslator(MetadataType.BOOLEAN, (entity, entityMetadata) -> entity.setFlag(EntityFlag.POWERED, ((BooleanEntityMetadata) entityMetadata).getPrimitiveValue())) + .addTranslator(MetadataType.BOOLEAN, CreeperEntity::setIgnited) + .build(); + DOLPHIN = EntityDefinition.inherited(WaterEntity::new, mobEntityBase) + .type(EntityType.DOLPHIN) + .bedrockId(31) + .height(0.6f).width(0.9f) + //TODO check + .addTranslator(null) // treasure position + .addTranslator(null) // "got fish" + .addTranslator(null) // "moistness level" + .build(); + ENDERMAN = EntityDefinition.inherited(EndermanEntity::new, mobEntityBase) + .type(EntityType.ENDERMAN) + .bedrockId(38) + .height(2.9f).width(0.6f) + .addTranslator(MetadataType.BLOCK_STATE, EndermanEntity::setCarriedBlock) + .addTranslator(MetadataType.BOOLEAN, EndermanEntity::setScreaming) + .addTranslator(MetadataType.BOOLEAN, EndermanEntity::setAngry) + .build(); + ENDERMITE = EntityDefinition.inherited(MonsterEntity::new, mobEntityBase) + .type(EntityType.ENDERMITE) + .bedrockId(55) + .height(0.3f).width(0.4f) + .build(); + ENDER_DRAGON = EntityDefinition.inherited(EnderDragonEntity::new, mobEntityBase) + .type(EntityType.ENDER_DRAGON) + .bedrockId(53) + .addTranslator(MetadataType.INT, EnderDragonEntity::setPhase) + .build(); + GHAST = EntityDefinition.inherited(GhastEntity::new, mobEntityBase) + .type(EntityType.GHAST) + .bedrockId(41) + .heightAndWidth(4.0f) + .addTranslator(MetadataType.BOOLEAN, GhastEntity::setGhastAttacking) + .build(); + GIANT = EntityDefinition.inherited(GiantEntity::new, mobEntityBase) + .type(EntityType.GIANT) + .bedrockId(32) + .height(1.8f).width(1.6f) + .offset(1.62f) + .identifier("minecraft:zombie") + .build(); + IRON_GOLEM = EntityDefinition.inherited(IronGolemEntity::new, mobEntityBase) + .type(EntityType.IRON_GOLEM) + .bedrockId(20) + .height(2.7f).width(1.4f) + .build(); + PHANTOM = EntityDefinition.inherited(PhantomEntity::new, mobEntityBase) + .type(EntityType.PHANTOM) + .bedrockId(58) + .height(0.5f).width(0.9f) + .offset(0.6f) + .addTranslator(MetadataType.INT, PhantomEntity::setPhantomScale) + .build(); + SILVERFISH = EntityDefinition.inherited(MonsterEntity::new, mobEntityBase) + .type(EntityType.SILVERFISH) + .bedrockId(39) + .height(0.3f).width(0.4f) + .build(); + SHULKER = EntityDefinition.inherited(ShulkerEntity::new, mobEntityBase) + .type(EntityType.SHULKER) + .bedrockId(54) + .heightAndWidth(1f) + .addTranslator(MetadataType.DIRECTION, ShulkerEntity::setAttachedFace) + .addTranslator(MetadataType.BYTE, ShulkerEntity::setShulkerHeight) + .addTranslator(MetadataType.BYTE, ShulkerEntity::setShulkerColor) + .build(); + SKELETON = EntityDefinition.inherited(SkeletonEntity::new, mobEntityBase) + .type(EntityType.SKELETON) + .bedrockId(34) + .height(1.8f).width(0.6f) + .offset(1.62f) + .addTranslator(MetadataType.BOOLEAN, SkeletonEntity::setConvertingToStray) + .build(); + SNOW_GOLEM = EntityDefinition.inherited(SnowGolemEntity::new, mobEntityBase) + .type(EntityType.SNOW_GOLEM) + .bedrockId(21) + .height(1.9f).width(0.7f) + .addTranslator(MetadataType.BYTE, SnowGolemEntity::setSnowGolemFlags) + .build(); + SPIDER = EntityDefinition.inherited(SpiderEntity::new, mobEntityBase) + .type(EntityType.SPIDER) + .bedrockId(35) + .height(0.9f).width(1.4f) + .offset(1f) + .addTranslator(MetadataType.BYTE, SpiderEntity::setSpiderFlags) + .build(); + SQUID = EntityDefinition.inherited(SquidEntity::new, mobEntityBase) + .type(EntityType.SQUID) + .bedrockId(17) + .heightAndWidth(0.8f) + .build(); + STRAY = EntityDefinition.inherited(AbstractSkeletonEntity::new, mobEntityBase) + .type(EntityType.STRAY) + .bedrockId(46) + .height(1.8f).width(0.6f) + .offset(1.62f) + .build(); + VEX = EntityDefinition.inherited(VexEntity::new, mobEntityBase) + .type(EntityType.VEX) + .bedrockId(105) + .height(0.8f).width(0.4f) + .addTranslator(MetadataType.BYTE, VexEntity::setVexFlags) + .build(); + WITHER = EntityDefinition.inherited(WitherEntity::new, mobEntityBase) + .type(EntityType.WITHER) + .bedrockId(52) + .height(3.5f).width(0.9f) + .addTranslator(MetadataType.INT, WitherEntity::setTarget1) + .addTranslator(MetadataType.INT, WitherEntity::setTarget2) + .addTranslator(MetadataType.INT, WitherEntity::setTarget3) + .addTranslator(MetadataType.INT, WitherEntity::setInvulnerableTicks) + .build(); + WITHER_SKELETON = EntityDefinition.inherited(AbstractSkeletonEntity::new, mobEntityBase) + .type(EntityType.WITHER_SKELETON) + .bedrockId(48) + .height(2.4f).width(0.7f) + .build(); + ZOGLIN = EntityDefinition.inherited(ZoglinEntity::new, mobEntityBase) + .type(EntityType.ZOGLIN) + .bedrockId(126) + .height(1.4f).width(1.3965f) + .addTranslator(MetadataType.BOOLEAN, ZoglinEntity::setBaby) + .build(); + ZOMBIE = EntityDefinition.inherited(ZombieEntity::new, mobEntityBase) + .type(EntityType.ZOMBIE) + .bedrockId(32) + .height(1.8f).width(0.6f) + .offset(1.62f) + .addTranslator(MetadataType.BOOLEAN, ZombieEntity::setZombieBaby) + .addTranslator(null) // "set special type", doesn't do anything + .addTranslator(MetadataType.BOOLEAN, ZombieEntity::setConvertingToDrowned) + .build(); + ZOMBIE_VILLAGER = EntityDefinition.inherited(ZombieVillagerEntity::new, ZOMBIE) + .type(EntityType.ZOMBIE_VILLAGER) + .bedrockId(44) + .height(1.8f).width(0.6f) + .offset(1.62f) + .identifier("minecraft:zombie_villager_v2") + .addTranslator(MetadataType.BOOLEAN, ZombieVillagerEntity::setTransforming) + .addTranslator(MetadataType.VILLAGER_DATA, ZombieVillagerEntity::setZombieVillagerData) + .build(); + ZOMBIFIED_PIGLIN = EntityDefinition.inherited(ZombifiedPiglinEntity::new, ZOMBIE) //TODO test how zombie entity metadata is handled? + .type(EntityType.ZOMBIFIED_PIGLIN) + .bedrockId(36) + .height(1.95f).width(0.6f) + .offset(1.62f) + .identifier("minecraft:zombie_pigman") + .build(); + + DROWNED = EntityDefinition.inherited(ZOMBIE.factory(), ZOMBIE) + .type(EntityType.DROWNED) + .bedrockId(110) + .height(1.95f).width(0.6f) + .build(); + HUSK = EntityDefinition.inherited(ZOMBIE.factory(), ZOMBIE) + .type(EntityType.HUSK) + .bedrockId(47) + .build(); + + GUARDIAN = EntityDefinition.inherited(GuardianEntity::new, mobEntityBase) + .type(EntityType.GUARDIAN) + .bedrockId(49) + .heightAndWidth(0.85f) + .addTranslator(null) // Moving //TODO + .addTranslator(MetadataType.INT, GuardianEntity::setGuardianTarget) + .build(); + ELDER_GUARDIAN = EntityDefinition.inherited(ElderGuardianEntity::new, GUARDIAN) + .type(EntityType.ELDER_GUARDIAN) + .bedrockId(50) + .heightAndWidth(1.9975f) + .build(); + + SLIME = EntityDefinition.inherited(SlimeEntity::new, mobEntityBase) + .type(EntityType.SLIME) + .bedrockId(37) + .heightAndWidth(0.51f) + .addTranslator(MetadataType.INT, SlimeEntity::setScale) + .build(); + MAGMA_CUBE = EntityDefinition.inherited(MagmaCubeEntity::new, SLIME) + .type(EntityType.MAGMA_CUBE) + .bedrockId(42) + .build(); + + EntityDefinition abstractFishEntityBase = EntityDefinition.inherited(AbstractFishEntity::new, mobEntityBase) + .addTranslator(null) // From bucket + .build(); + COD = EntityDefinition.inherited(abstractFishEntityBase.factory(), abstractFishEntityBase) + .type(EntityType.COD) + .bedrockId(112) + .height(0.25f).width(0.5f) + .build(); + PUFFERFISH = EntityDefinition.inherited(PufferFishEntity::new, abstractFishEntityBase) + .type(EntityType.PUFFERFISH) + .bedrockId(108) + .heightAndWidth(0.7f) + .addTranslator(MetadataType.INT, PufferFishEntity::setPufferfishSize) + .build(); + SALMON = EntityDefinition.inherited(abstractFishEntityBase.factory(), abstractFishEntityBase) + .type(EntityType.SALMON) + .bedrockId(109) + .height(0.5f).width(0.7f) + .build(); + TROPICAL_FISH = EntityDefinition.inherited(TropicalFishEntity::new, abstractFishEntityBase) + .type(EntityType.TROPICAL_FISH) + .bedrockId(111) + .heightAndWidth(0.6f) + .identifier("minecraft:tropicalfish") + .build(); + + EntityDefinition abstractPiglinEntityBase = EntityDefinition.inherited(BasePiglinEntity::new, mobEntityBase) + .addTranslator(MetadataType.BOOLEAN, BasePiglinEntity::setImmuneToZombification) + .build(); + PIGLIN = EntityDefinition.inherited(PiglinEntity::new, abstractPiglinEntityBase) + .type(EntityType.PIGLIN) + .bedrockId(123) + .height(1.95f).width(0.6f) + .addTranslator(MetadataType.BOOLEAN, PiglinEntity::setBaby) + .addTranslator(MetadataType.BOOLEAN, PiglinEntity::setChargingCrossbow) + .addTranslator(MetadataType.BOOLEAN, PiglinEntity::setDancing) + .build(); + PIGLIN_BRUTE = EntityDefinition.inherited(abstractPiglinEntityBase.factory(), abstractPiglinEntityBase) + .type(EntityType.PIGLIN_BRUTE) + .bedrockId(127) + .height(1.95f).width(0.6f) + .build(); + + GLOW_SQUID = EntityDefinition.inherited(GlowSquidEntity::new, SQUID) + .type(EntityType.GLOW_SQUID) + .addTranslator(null) // Set dark ticks remaining, possible TODO + .build(); + + EntityDefinition raidParticipantEntityBase = EntityDefinition.inherited(RaidParticipantEntity::new, mobEntityBase) + .addTranslator(null) // Celebrating //TODO + .build(); + EntityDefinition spellcasterEntityBase = EntityDefinition.inherited(SpellcasterIllagerEntity::new, raidParticipantEntityBase) + .addTranslator(MetadataType.BYTE, SpellcasterIllagerEntity::setSpellType) + .build(); + EVOKER = EntityDefinition.inherited(spellcasterEntityBase.factory(), spellcasterEntityBase) + .type(EntityType.EVOKER) + .bedrockId(104) + .height(1.95f).width(0.6f) + .identifier("minecraft:evocation_illager") + .build(); + ILLUSIONER = EntityDefinition.inherited(spellcasterEntityBase.factory(), spellcasterEntityBase) + .type(EntityType.ILLUSIONER) + .bedrockId(104) + .height(1.95f).width(0.6f) + .identifier("minecraft:evocation_illager") + .build(); + PILLAGER = EntityDefinition.inherited(PillagerEntity::new, raidParticipantEntityBase) + .type(EntityType.PILLAGER) + .bedrockId(114) + .height(1.8f).width(0.6f) + .offset(1.62f) + .addTranslator(null) // Charging; doesn't have an equivalent on Bedrock //TODO check + .build(); + RAVAGER = EntityDefinition.inherited(raidParticipantEntityBase.factory(), raidParticipantEntityBase) + .type(EntityType.RAVAGER) + .bedrockId(59) + .height(1.9f).width(1.2f) + .build(); + VINDICATOR = EntityDefinition.inherited(VindicatorEntity::new, raidParticipantEntityBase) + .type(EntityType.VINDICATOR) + .bedrockId(57) + .height(1.8f).width(0.6f) + .offset(1.62f) + .build(); + WITCH = EntityDefinition.inherited(raidParticipantEntityBase.factory(), raidParticipantEntityBase) + .type(EntityType.WITCH) + .bedrockId(45) + .height(1.8f).width(0.6f) + .offset(1.62f) + .build(); + } + + EntityDefinition ageableEntityBase = EntityDefinition.inherited(AgeableEntity::new, mobEntityBase) + .addTranslator(MetadataType.BOOLEAN, AgeableEntity::setBaby) + .build(); + + // Extends ageable + { + AXOLOTL = EntityDefinition.inherited(AxolotlEntity::new, ageableEntityBase) + .type(EntityType.AXOLOTL) + .height(0.42f).width(0.7f) + .addTranslator(MetadataType.INT, AxolotlEntity::setVariant) + .addTranslator(MetadataType.BOOLEAN, AxolotlEntity::setPlayingDead) + .build(); + BEE = EntityDefinition.inherited(BeeEntity::new, ageableEntityBase) + .type(EntityType.BEE) + .bedrockId(122) + .heightAndWidth(0.6f) + .addTranslator(MetadataType.BYTE, BeeEntity::setBeeFlags) + .addTranslator(MetadataType.INT, BeeEntity::setAngerTime) + .build(); + CHICKEN = EntityDefinition.inherited(ChickenEntity::new, ageableEntityBase) + .type(EntityType.CHICKEN) + .bedrockId(10) + .height(0.7f).width(0.4f) + .build(); + COW = EntityDefinition.inherited(AnimalEntity::new, ageableEntityBase) + .type(EntityType.COW) + .bedrockId(11) + .height(1.4f).width(0.9f) + .build(); + FOX = EntityDefinition.inherited(FoxEntity::new, ageableEntityBase) + .type(EntityType.FOX) + .bedrockId(121) + .height(0.5f).width(1.25f) + .addTranslator(MetadataType.INT, FoxEntity::setFoxVariant) + .addTranslator(MetadataType.BYTE, FoxEntity::setFoxFlags) + .build(); + HOGLIN = EntityDefinition.inherited(HoglinEntity::new, ageableEntityBase) + .type(EntityType.HOGLIN) + .bedrockId(124) + .height(1.4f).width(1.3965f) + .addTranslator(MetadataType.BOOLEAN, HoglinEntity::setImmuneToZombification) + .build(); + GOAT = EntityDefinition.inherited(GoatEntity::new, ageableEntityBase) + .type(EntityType.GOAT) + .height(1.3f).width(0.9f) + .addTranslator(MetadataType.BOOLEAN, GoatEntity::setScreamer) + .build(); + MOOSHROOM = EntityDefinition.inherited(MooshroomEntity::new, ageableEntityBase) // TODO remove class + .type(EntityType.MOOSHROOM) + .bedrockId(16) + .height(1.4f).width(0.9f) + .addTranslator(MetadataType.STRING, (entity, entityMetadata) -> entity.getDirtyMetadata().put(EntityData.VARIANT, entityMetadata.getValue().equals("brown") ? 1 : 0)) + .build(); + OCELOT = EntityDefinition.inherited(OcelotEntity::new, ageableEntityBase) + .type(EntityType.OCELOT) + .bedrockId(22) + .height(0.35f).width(0.3f) + .addTranslator(MetadataType.BOOLEAN, (ocelotEntity, entityMetadata) -> ocelotEntity.setFlag(EntityFlag.TRUSTING, ((BooleanEntityMetadata) entityMetadata).getPrimitiveValue())) + .build(); + PANDA = EntityDefinition.inherited(PandaEntity::new, ageableEntityBase) + .type(EntityType.PANDA) + .bedrockId(113) + .height(1.25f).width(1.125f) + .addTranslator(null) // Unhappy counter + .addTranslator(null) // Sneeze counter + .addTranslator(MetadataType.INT, PandaEntity::setEatingCounter) + .addTranslator(MetadataType.BYTE, PandaEntity::setMainGene) + .addTranslator(MetadataType.BYTE, PandaEntity::setHiddenGene) + .addTranslator(MetadataType.BYTE, PandaEntity::setPandaFlags) + .build(); + PIG = EntityDefinition.inherited(PigEntity::new, ageableEntityBase) + .type(EntityType.PIG) + .bedrockId(12) + .heightAndWidth(0.9f) + .addTranslator(MetadataType.BOOLEAN, (pigEntity, entityMetadata) -> pigEntity.setFlag(EntityFlag.SADDLED, ((BooleanEntityMetadata) entityMetadata).getPrimitiveValue())) + .addTranslator(null) // Boost time + .build(); + POLAR_BEAR = EntityDefinition.inherited(PolarBearEntity::new, ageableEntityBase) + .type(EntityType.POLAR_BEAR) + .bedrockId(28) + .height(1.4f).width(1.3f) + .addTranslator(MetadataType.BOOLEAN, (entity, entityMetadata) -> entity.setFlag(EntityFlag.STANDING, ((BooleanEntityMetadata) entityMetadata).getPrimitiveValue())) + .build(); + RABBIT = EntityDefinition.inherited(RabbitEntity::new, ageableEntityBase) + .type(EntityType.RABBIT) + .bedrockId(18) + .height(0.5f).width(0.4f) + .addTranslator(MetadataType.INT, RabbitEntity::setRabbitVariant) + .build(); + SHEEP = EntityDefinition.inherited(SheepEntity::new, ageableEntityBase) + .type(EntityType.SHEEP) + .bedrockId(13) + .heightAndWidth(0.9f) + .addTranslator(MetadataType.BYTE, SheepEntity::setSheepFlags) + .build(); + STRIDER = EntityDefinition.inherited(StriderEntity::new, ageableEntityBase) + .type(EntityType.STRIDER) + .bedrockId(125) + .height(1.7f).width(0.9f) + .addTranslator(null) // Boost time + .addTranslator(MetadataType.BOOLEAN, StriderEntity::setCold) + .addTranslator(MetadataType.BOOLEAN, StriderEntity::setSaddled) + .build(); + TURTLE = EntityDefinition.inherited(TurtleEntity::new, ageableEntityBase) + .type(EntityType.TURTLE) + .bedrockId(74) + .height(0.4f).width(1.2f) + .addTranslator(MetadataType.BOOLEAN, TurtleEntity::setPregnant) + .addTranslator(MetadataType.BOOLEAN, TurtleEntity::setLayingEgg) + .build(); + + EntityDefinition abstractVillagerEntityBase = EntityDefinition.inherited(AbstractMerchantEntity::new, ageableEntityBase) + .addTranslator(null) // Unhappy ticks + .build(); + VILLAGER = EntityDefinition.inherited(VillagerEntity::new, abstractVillagerEntityBase) + .type(EntityType.VILLAGER) + .bedrockId(15) + .height(1.8f).width(0.6f) + .offset(1.62f) + .identifier("minecraft:villager_v2") + .addTranslator(MetadataType.VILLAGER_DATA, VillagerEntity::setVillagerData) + .build(); + WANDERING_TRADER = EntityDefinition.inherited(abstractVillagerEntityBase.factory(), abstractVillagerEntityBase) + .type(EntityType.WANDERING_TRADER) + .bedrockId(118) + .height(1.8f).width(0.6f) + .offset(1.62f) + .build(); + } + + // Horses + { + EntityDefinition abstractHorseEntityBase = EntityDefinition.inherited(AbstractHorseEntity::new, ageableEntityBase) + .addTranslator(MetadataType.BYTE, AbstractHorseEntity::setHorseFlags) + .addTranslator(null) // UUID of owner + .build(); + HORSE = EntityDefinition.inherited(HorseEntity::new, abstractHorseEntityBase) + .type(EntityType.HORSE) + .bedrockId(23) + .height(1.6f).width(1.3965f) + .addTranslator(MetadataType.BYTE, HorseEntity::setHorseVariant) + .build(); + SKELETON_HORSE = EntityDefinition.inherited(abstractHorseEntityBase.factory(), abstractHorseEntityBase) + .type(EntityType.SKELETON_HORSE) + .bedrockId(26) + .height(1.6f).width(1.3965f) + .build(); + ZOMBIE_HORSE = EntityDefinition.inherited(abstractHorseEntityBase.factory(), abstractHorseEntityBase) + .type(EntityType.ZOMBIE_HORSE) + .bedrockId(27) + .height(1.6f).width(1.3965f) + .build(); + EntityDefinition chestedHorseEntityBase = EntityDefinition.inherited(ChestedHorseEntity::new, abstractHorseEntityBase) + .addTranslator(MetadataType.BOOLEAN, (horseEntity, entityMetadata) -> horseEntity.setFlag(EntityFlag.CHESTED, ((BooleanEntityMetadata) entityMetadata).getPrimitiveValue())) + .build(); + DONKEY = EntityDefinition.inherited(chestedHorseEntityBase.factory(), chestedHorseEntityBase) + .type(EntityType.DONKEY) + .bedrockId(24) + .height(1.6f).width(1.3965f) + .build(); + MULE = EntityDefinition.inherited(chestedHorseEntityBase.factory(), chestedHorseEntityBase) + .type(EntityType.MULE) + .bedrockId(25) + .height(1.6f).width(1.3965f) + .build(); + LLAMA = EntityDefinition.inherited(LlamaEntity::new, chestedHorseEntityBase) + .type(EntityType.LLAMA) + .bedrockId(29) + .height(1.87f).width(0.9f) + .addTranslator(MetadataType.INT, (entity, entityMetadata) -> entity.getDirtyMetadata().put(EntityData.STRENGTH, entityMetadata.getValue())) + .addTranslator(MetadataType.INT, LlamaEntity::setCarpetedColor) + .addTranslator(MetadataType.INT, (entity, entityMetadata) -> entity.getDirtyMetadata().put(EntityData.VARIANT, entityMetadata.getValue())) + .build(); + TRADER_LLAMA = EntityDefinition.inherited(TraderLlamaEntity::new, LLAMA) + .type(EntityType.TRADER_LLAMA) + .identifier("minecraft:llama") + .build(); + } + + EntityDefinition tameableEntityBase = EntityDefinition.inherited(TameableEntity::new, ageableEntityBase) + .addTranslator(MetadataType.BYTE, TameableEntity::setTameableFlags) + .addTranslator(MetadataType.OPTIONAL_UUID, TameableEntity::setOwner) + .build(); + CAT = EntityDefinition.inherited(CatEntity::new, tameableEntityBase) + .type(EntityType.CAT) + .bedrockId(75) + .height(0.35f).width(0.3f) + .addTranslator(MetadataType.INT, CatEntity::setCatVariant) + .addTranslator(MetadataType.BOOLEAN, CatEntity::setResting) + .addTranslator(null) // "resting state one" //TODO + .addTranslator(MetadataType.INT, CatEntity::setCollarColor) + .build(); + PARROT = EntityDefinition.inherited(ParrotEntity::new, tameableEntityBase) + .type(EntityType.PARROT) + .bedrockId(30) + .height(0.9f).width(0.5f) + .addTranslator(MetadataType.INT, (parrotEntity, entityMetadata) -> parrotEntity.getDirtyMetadata().put(EntityData.VARIANT, entityMetadata.getValue())) // Parrot color + .build(); + WOLF = EntityDefinition.inherited(WolfEntity::new, tameableEntityBase) + .type(EntityType.WOLF) + .bedrockId(14) + .height(0.85f).width(0.6f) + // "Begging" on wiki.vg, "Interested" in Nukkit - the tilt of the head + .addTranslator(MetadataType.BOOLEAN, (wolfEntity, entityMetadata) -> wolfEntity.setFlag(EntityFlag.INTERESTED, ((BooleanEntityMetadata) entityMetadata).getPrimitiveValue())) + .addTranslator(MetadataType.INT, WolfEntity::setCollarColor) + .addTranslator(MetadataType.INT, WolfEntity::setWolfAngerTime) + .build(); + + // As of 1.18 these don't track entity data at all + ENDER_DRAGON_PART = EntityDefinition.builder(null) + .bedrockId(32) + .identifier("minecraft:armor_stand") // Emulated + .build(); + } + + public static void init() { + // no-op + } + + private EntityDefinitions() { + } +} diff --git a/connector/src/main/java/org/geysermc/connector/entity/living/InsentientEntity.java b/connector/src/main/java/org/geysermc/connector/entity/EntityMetadataTranslator.java similarity index 57% rename from connector/src/main/java/org/geysermc/connector/entity/living/InsentientEntity.java rename to connector/src/main/java/org/geysermc/connector/entity/EntityMetadataTranslator.java index d0f99cdb9..e0936559d 100644 --- a/connector/src/main/java/org/geysermc/connector/entity/living/InsentientEntity.java +++ b/connector/src/main/java/org/geysermc/connector/entity/EntityMetadataTranslator.java @@ -23,29 +23,18 @@ * @link https://github.com/GeyserMC/Geyser */ -package org.geysermc.connector.entity.living; +package org.geysermc.connector.entity; import com.github.steveice10.mc.protocol.data.game.entity.metadata.EntityMetadata; import com.github.steveice10.mc.protocol.data.game.entity.metadata.MetadataType; -import com.nukkitx.math.vector.Vector3f; -import com.nukkitx.protocol.bedrock.data.entity.EntityFlag; -import org.geysermc.connector.entity.LivingEntity; -import org.geysermc.connector.entity.type.EntityType; -import org.geysermc.connector.network.session.GeyserSession; -public class InsentientEntity extends LivingEntity { +import java.util.function.BiConsumer; - public InsentientEntity(long entityId, long geyserId, EntityType entityType, Vector3f position, Vector3f motion, Vector3f rotation) { - super(entityId, geyserId, entityType, position, motion, rotation); - } +/** + * Translates a given Java {@link EntityMetadata} into a similar/same construct for Bedrock + */ - @Override - public void updateBedrockMetadata(EntityMetadata entityMetadata, GeyserSession session) { - if (entityMetadata.getId() == 15 && entityMetadata.getType() == MetadataType.BYTE) { - byte xd = (byte) entityMetadata.getValue(); - metadata.getFlags().setFlag(EntityFlag.NO_AI, (xd & 0x01) == 0x01); - } - - super.updateBedrockMetadata(entityMetadata, session); - } +public record EntityMetadataTranslator( + MetadataType acceptedType, + BiConsumer> translateFunction) { } diff --git a/connector/src/main/java/org/geysermc/connector/entity/ExpOrbEntity.java b/connector/src/main/java/org/geysermc/connector/entity/ExpOrbEntity.java index f6fbcde9d..14dacc4cd 100644 --- a/connector/src/main/java/org/geysermc/connector/entity/ExpOrbEntity.java +++ b/connector/src/main/java/org/geysermc/connector/entity/ExpOrbEntity.java @@ -27,22 +27,21 @@ package org.geysermc.connector.entity; import com.nukkitx.math.vector.Vector3f; import com.nukkitx.protocol.bedrock.data.entity.EntityData; -import org.geysermc.connector.entity.type.EntityType; import org.geysermc.connector.network.session.GeyserSession; public class ExpOrbEntity extends Entity { private final int amount; - public ExpOrbEntity(int amount, long entityId, long geyserId, EntityType entityType, Vector3f position, Vector3f motion, Vector3f rotation) { - super(entityId, geyserId, entityType, position, motion, rotation); + public ExpOrbEntity(GeyserSession session, int amount, long entityId, long geyserId, Vector3f position) { + super(session, entityId, geyserId, null, EntityDefinitions.EXPERIENCE_ORB, position, Vector3f.ZERO, 0, 0, 0); this.amount = amount; } @Override - public void spawnEntity(GeyserSession session) { - this.metadata.put(EntityData.EXPERIENCE_VALUE, amount); - super.spawnEntity(session); + protected void initializeMetadata() { + super.initializeMetadata(); + this.dirtyMetadata.put(EntityData.EXPERIENCE_VALUE, amount); } } diff --git a/connector/src/main/java/org/geysermc/connector/entity/FallingBlockEntity.java b/connector/src/main/java/org/geysermc/connector/entity/FallingBlockEntity.java index a82118c21..5c5b4dde8 100644 --- a/connector/src/main/java/org/geysermc/connector/entity/FallingBlockEntity.java +++ b/connector/src/main/java/org/geysermc/connector/entity/FallingBlockEntity.java @@ -26,31 +26,32 @@ package org.geysermc.connector.entity; import com.github.steveice10.mc.protocol.data.game.entity.metadata.EntityMetadata; +import com.github.steveice10.mc.protocol.data.game.entity.metadata.type.BooleanEntityMetadata; import com.nukkitx.math.vector.Vector3f; import com.nukkitx.protocol.bedrock.data.entity.EntityData; import com.nukkitx.protocol.bedrock.data.entity.EntityFlag; -import org.geysermc.connector.entity.type.EntityType; import org.geysermc.connector.network.session.GeyserSession; +import java.util.UUID; + public class FallingBlockEntity extends Entity { private final int javaId; - public FallingBlockEntity(long entityId, long geyserId, EntityType entityType, Vector3f position, Vector3f motion, Vector3f rotation, int javaId) { - super(entityId, geyserId, entityType, position, motion, rotation); + public FallingBlockEntity(GeyserSession session, long entityId, long geyserId, UUID uuid, Vector3f position, Vector3f motion, float yaw, float pitch, int javaId) { + super(session, entityId, geyserId, uuid, EntityDefinitions.FALLING_BLOCK, position, motion, yaw, pitch, 0f); this.javaId = javaId; } @Override - public void spawnEntity(GeyserSession session) { - this.metadata.put(EntityData.VARIANT, session.getBlockMappings().getBedrockBlockId(javaId)); - super.spawnEntity(session); + protected void initializeMetadata() { + super.initializeMetadata(); + this.dirtyMetadata.put(EntityData.VARIANT, session.getBlockMappings().getBedrockBlockId(javaId)); } @Override - public void updateBedrockMetadata(EntityMetadata entityMetadata, GeyserSession session) { + public void setGravity(EntityMetadata entityMetadata) { + super.setGravity(entityMetadata); // Set the NO_AI flag based on the no gravity flag to prevent movement - if (entityMetadata.getId() == 5) { - this.metadata.getFlags().setFlag(EntityFlag.NO_AI, (boolean) entityMetadata.getValue()); - } + setFlag(EntityFlag.NO_AI, ((BooleanEntityMetadata) entityMetadata).getPrimitiveValue()); } } diff --git a/connector/src/main/java/org/geysermc/connector/entity/FireworkEntity.java b/connector/src/main/java/org/geysermc/connector/entity/FireworkEntity.java index 9de2102d5..fae7ca421 100644 --- a/connector/src/main/java/org/geysermc/connector/entity/FireworkEntity.java +++ b/connector/src/main/java/org/geysermc/connector/entity/FireworkEntity.java @@ -37,7 +37,6 @@ import com.nukkitx.nbt.NbtType; import com.nukkitx.protocol.bedrock.data.entity.EntityData; import com.nukkitx.protocol.bedrock.packet.SetEntityMotionPacket; 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.utils.FireworkColor; import org.geysermc.connector.utils.MathUtils; @@ -46,118 +45,116 @@ import org.geysermc.floodgate.util.DeviceOs; import java.util.ArrayList; import java.util.List; import java.util.OptionalInt; +import java.util.UUID; public class FireworkEntity extends Entity { - public FireworkEntity(long entityId, long geyserId, EntityType entityType, Vector3f position, Vector3f motion, Vector3f rotation) { - super(entityId, geyserId, entityType, position, motion, rotation); + public FireworkEntity(GeyserSession session, long 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 - public void updateBedrockMetadata(EntityMetadata entityMetadata, GeyserSession session) { - if (entityMetadata.getId() == 8) { - ItemStack item = (ItemStack) entityMetadata.getValue(); - if (item == null) { - return; - } - CompoundTag tag = item.getNbt(); + public void setFireworkItem(EntityMetadata entityMetadata) { + ItemStack item = entityMetadata.getValue(); + if (item == null) { + return; + } + CompoundTag tag = item.getNbt(); - if (tag == null) { - return; - } + if (tag == null) { + return; + } - // TODO: Remove once Mojang fixes bugs with fireworks crashing clients on these specific devices. - // https://bugs.mojang.com/browse/MCPE-89115 - if (session.getClientData().getDeviceOs() == DeviceOs.XBOX - || session.getClientData().getDeviceOs() == DeviceOs.PS4) { - return; - } + // TODO: Remove once Mojang fixes bugs with fireworks crashing clients on these specific devices. + // https://bugs.mojang.com/browse/MCPE-89115 + if (session.getClientData().getDeviceOs() == DeviceOs.XBOX + || session.getClientData().getDeviceOs() == DeviceOs.PS4) { + return; + } - CompoundTag fireworks = tag.get("Fireworks"); - if (fireworks == null) { - // Thank you Mineplex very cool - return; - } + CompoundTag fireworks = tag.get("Fireworks"); + if (fireworks == null) { + // Thank you Mineplex very cool + return; + } - NbtMapBuilder fireworksBuilder = NbtMap.builder(); - if (fireworks.get("Flight") != null) { - fireworksBuilder.putByte("Flight", MathUtils.getNbtByte(fireworks.get("Flight").getValue())); - } + NbtMapBuilder fireworksBuilder = NbtMap.builder(); + if (fireworks.get("Flight") != null) { + fireworksBuilder.putByte("Flight", MathUtils.getNbtByte(fireworks.get("Flight").getValue())); + } - List explosions = new ArrayList<>(); - if (fireworks.get("Explosions") != null) { - for (Tag effect : ((ListTag) fireworks.get("Explosions")).getValue()) { - CompoundTag effectData = (CompoundTag) effect; - NbtMapBuilder effectBuilder = NbtMap.builder(); + List explosions = new ArrayList<>(); + if (fireworks.get("Explosions") != null) { + for (Tag effect : ((ListTag) fireworks.get("Explosions")).getValue()) { + CompoundTag effectData = (CompoundTag) effect; + NbtMapBuilder effectBuilder = NbtMap.builder(); - if (effectData.get("Type") != null) { - effectBuilder.putByte("FireworkType", MathUtils.getNbtByte(effectData.get("Type").getValue())); - } - - if (effectData.get("Colors") != null) { - int[] oldColors = (int[]) effectData.get("Colors").getValue(); - byte[] colors = new byte[oldColors.length]; - - int i = 0; - for (int color : oldColors) { - colors[i++] = FireworkColor.fromJavaRGB(color); - } - - effectBuilder.putByteArray("FireworkColor", colors); - } - - if (effectData.get("FadeColors") != null) { - int[] oldColors = (int[]) effectData.get("FadeColors").getValue(); - byte[] colors = new byte[oldColors.length]; - - int i = 0; - for (int color : oldColors) { - colors[i++] = FireworkColor.fromJavaRGB(color); - } - - effectBuilder.putByteArray("FireworkFade", colors); - } - - if (effectData.get("Trail") != null) { - effectBuilder.putByte("FireworkTrail", MathUtils.getNbtByte(effectData.get("Trail").getValue())); - } - - if (effectData.get("Flicker") != null) { - effectBuilder.putByte("FireworkFlicker", MathUtils.getNbtByte(effectData.get("Flicker").getValue())); - } - - explosions.add(effectBuilder.build()); + if (effectData.get("Type") != null) { + effectBuilder.putByte("FireworkType", MathUtils.getNbtByte(effectData.get("Type").getValue())); } - } - fireworksBuilder.putList("Explosions", NbtType.COMPOUND, explosions); + if (effectData.get("Colors") != null) { + int[] oldColors = (int[]) effectData.get("Colors").getValue(); + byte[] colors = new byte[oldColors.length]; - NbtMapBuilder builder = NbtMap.builder(); - builder.put("Fireworks", fireworksBuilder.build()); - metadata.put(EntityData.DISPLAY_ITEM, builder.build()); - } else if (entityMetadata.getId() == 9) { - OptionalInt optional = (OptionalInt) entityMetadata.getValue(); - // Checks if the firework has an entity ID (used when a player is gliding) - // and checks to make sure the player that is gliding is the one getting sent the packet - // or else every player near the gliding player will boost too. - if (optional.isPresent() && optional.getAsInt() == session.getPlayerEntity().getEntityId()) { - PlayerEntity entity = session.getPlayerEntity(); - float yaw = entity.getRotation().getX(); - float pitch = entity.getRotation().getY(); - // Uses math from NukkitX - entity.setMotion(Vector3f.from( - -Math.sin(Math.toRadians(yaw)) * Math.cos(Math.toRadians(pitch)) * 2, - -Math.sin(Math.toRadians(pitch)) * 2, - Math.cos(Math.toRadians(yaw)) * Math.cos(Math.toRadians(pitch)) * 2)); - // Need to update the EntityMotionPacket or else the player won't boost - SetEntityMotionPacket entityMotionPacket = new SetEntityMotionPacket(); - entityMotionPacket.setRuntimeEntityId(entity.getGeyserId()); - entityMotionPacket.setMotion(entity.getMotion()); + int i = 0; + for (int color : oldColors) { + colors[i++] = FireworkColor.fromJavaRGB(color); + } - session.sendUpstreamPacket(entityMotionPacket); + effectBuilder.putByteArray("FireworkColor", colors); + } + + if (effectData.get("FadeColors") != null) { + int[] oldColors = (int[]) effectData.get("FadeColors").getValue(); + byte[] colors = new byte[oldColors.length]; + + int i = 0; + for (int color : oldColors) { + colors[i++] = FireworkColor.fromJavaRGB(color); + } + + effectBuilder.putByteArray("FireworkFade", colors); + } + + if (effectData.get("Trail") != null) { + effectBuilder.putByte("FireworkTrail", MathUtils.getNbtByte(effectData.get("Trail").getValue())); + } + + if (effectData.get("Flicker") != null) { + effectBuilder.putByte("FireworkFlicker", MathUtils.getNbtByte(effectData.get("Flicker").getValue())); + } + + explosions.add(effectBuilder.build()); } } - super.updateBedrockMetadata(entityMetadata, session); + fireworksBuilder.putList("Explosions", NbtType.COMPOUND, explosions); + + NbtMapBuilder builder = NbtMap.builder(); + builder.put("Fireworks", fireworksBuilder.build()); + dirtyMetadata.put(EntityData.DISPLAY_ITEM, builder.build()); + } + + public void setPlayerGliding(EntityMetadata entityMetadata) { + OptionalInt optional = entityMetadata.getValue(); + // Checks if the firework has an entity ID (used when a player is gliding) + // and checks to make sure the player that is gliding is the one getting sent the packet + // or else every player near the gliding player will boost too. + if (optional.isPresent() && optional.getAsInt() == session.getPlayerEntity().getEntityId()) { + PlayerEntity entity = session.getPlayerEntity(); + float yaw = entity.getYaw(); + float pitch = entity.getPitch(); + // Uses math from NukkitX + entity.setMotion(Vector3f.from( + -Math.sin(Math.toRadians(yaw)) * Math.cos(Math.toRadians(pitch)) * 2, + -Math.sin(Math.toRadians(pitch)) * 2, + Math.cos(Math.toRadians(yaw)) * Math.cos(Math.toRadians(pitch)) * 2)); + // Need to update the EntityMotionPacket or else the player won't boost + SetEntityMotionPacket entityMotionPacket = new SetEntityMotionPacket(); + entityMotionPacket.setRuntimeEntityId(entity.getGeyserId()); + entityMotionPacket.setMotion(entity.getMotion()); + + session.sendUpstreamPacket(entityMotionPacket); + } } } diff --git a/connector/src/main/java/org/geysermc/connector/entity/FishingHookEntity.java b/connector/src/main/java/org/geysermc/connector/entity/FishingHookEntity.java index 565614143..2493ef588 100644 --- a/connector/src/main/java/org/geysermc/connector/entity/FishingHookEntity.java +++ b/connector/src/main/java/org/geysermc/connector/entity/FishingHookEntity.java @@ -26,12 +26,12 @@ package org.geysermc.connector.entity; import com.github.steveice10.mc.protocol.data.game.entity.metadata.EntityMetadata; +import com.github.steveice10.mc.protocol.data.game.entity.metadata.type.IntEntityMetadata; import com.nukkitx.math.vector.Vector3f; import com.nukkitx.protocol.bedrock.data.entity.EntityData; import com.nukkitx.protocol.bedrock.data.entity.EntityFlag; import com.nukkitx.protocol.bedrock.packet.PlaySoundPacket; 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.collision.BoundingBox; import org.geysermc.connector.network.translators.collision.translators.BlockCollision; @@ -40,6 +40,7 @@ import org.geysermc.connector.registry.BlockRegistries; import org.geysermc.connector.utils.BlockPositionIterator; import org.geysermc.connector.utils.BlockUtils; +import java.util.UUID; import java.util.concurrent.ThreadLocalRandom; public class FishingHookEntity extends ThrowableEntity { @@ -50,41 +51,44 @@ public class FishingHookEntity extends ThrowableEntity { private boolean inWater = false; - public FishingHookEntity(long entityId, long geyserId, EntityType entityType, Vector3f position, Vector3f motion, Vector3f rotation, PlayerEntity owner) { - super(entityId, geyserId, entityType, position, motion, rotation); + public FishingHookEntity(GeyserSession session, long entityId, long geyserId, UUID uuid, Vector3f position, Vector3f motion, float yaw, float pitch, PlayerEntity owner) { + super(session, entityId, geyserId, uuid, EntityDefinitions.FISHING_BOBBER, position, motion, yaw, pitch, 0f); this.boundingBox = new BoundingBox(0.125, 0.125, 0.125, 0.25, 0.25, 0.25); // In Java, the splash sound depends on the entity's velocity, but in Bedrock the volume doesn't change. // This splash can be confused with the sound from catching a fish. This silences the splash from Bedrock, // so that it can be handled by moveAbsoluteImmediate. - this.metadata.putFloat(EntityData.BOUNDING_BOX_HEIGHT, 128); + this.dirtyMetadata.putFloat(EntityData.BOUNDING_BOX_HEIGHT, 128); - this.metadata.put(EntityData.OWNER_EID, owner.getGeyserId()); + this.dirtyMetadata.put(EntityData.OWNER_EID, owner.getGeyserId()); } @Override - public void updateBedrockMetadata(EntityMetadata entityMetadata, GeyserSession session) { - if (entityMetadata.getId() == 8) { // Hooked entity - int hookedEntityId = (int) entityMetadata.getValue() - 1; - Entity entity = session.getEntityCache().getEntityByJavaId(hookedEntityId); - if (entity == null && session.getPlayerEntity().getEntityId() == hookedEntityId) { - entity = session.getPlayerEntity(); - } + public void spawnEntity() { - if (entity != null) { - metadata.put(EntityData.TARGET_EID, entity.getGeyserId()); - hooked = true; - } else { - hooked = false; - } + super.spawnEntity(); + } + + public void setHookedEntity(EntityMetadata entityMetadata) { + int hookedEntityId = ((IntEntityMetadata) entityMetadata).getPrimitiveValue() - 1; + Entity entity; + if (session.getPlayerEntity().getEntityId() == hookedEntityId) { + entity = session.getPlayerEntity(); + } else { + entity = session.getEntityCache().getEntityByJavaId(hookedEntityId); } - super.updateBedrockMetadata(entityMetadata, session); + if (entity != null) { + dirtyMetadata.put(EntityData.TARGET_EID, entity.getGeyserId()); + hooked = true; + } else { + hooked = false; + } } @Override - protected void moveAbsoluteImmediate(GeyserSession session, Vector3f position, Vector3f rotation, boolean isOnGround, boolean teleported) { + protected void moveAbsoluteImmediate(Vector3f position, float yaw, float pitch, float headYaw, boolean isOnGround, boolean teleported) { boundingBox.setMiddleX(position.getX()); boundingBox.setMiddleY(position.getY() + boundingBox.getSizeY() / 2); boundingBox.setMiddleZ(position.getZ()); @@ -123,14 +127,14 @@ public class FishingHookEntity extends ThrowableEntity { inWater = touchingWater; if (!collided) { - super.moveAbsoluteImmediate(session, position, rotation, isOnGround, teleported); + super.moveAbsoluteImmediate(position, yaw, pitch, headYaw, isOnGround, teleported); } else { - super.moveAbsoluteImmediate(session, this.position, rotation, true, true); + super.moveAbsoluteImmediate(this.position, yaw, pitch, headYaw, true, true); } } private void sendSplashSound(GeyserSession session) { - if (!metadata.getFlags().getFlag(EntityFlag.SILENT)) { + if (!getFlag(EntityFlag.SILENT)) { float volume = (float) (0.2f * Math.sqrt(0.2 * (motion.getX() * motion.getX() + motion.getZ() * motion.getZ()) + motion.getY() * motion.getY())); if (volume > 1) { volume = 1; @@ -145,39 +149,38 @@ public class FishingHookEntity extends ThrowableEntity { } @Override - public void tick(GeyserSession session) { - if (hooked || !isInAir(session) && !isInWater(session) || isOnGround()) { + public void tick() { + if (hooked || !isInAir() && !isInWater() || isOnGround()) { motion = Vector3f.ZERO; return; } - float gravity = getGravity(session); + float gravity = getGravity(); motion = motion.down(gravity); - moveAbsoluteImmediate(session, position.add(motion), rotation, onGround, false); + moveAbsoluteImmediate(position.add(motion), yaw, pitch, headYaw, onGround, false); - float drag = getDrag(session); + float drag = getDrag(); motion = motion.mul(drag); } @Override - protected float getGravity(GeyserSession session) { - if (!isInWater(session) && !onGround) { + protected float getGravity() { + if (!isInWater() && !onGround) { return 0.03f; } return 0; } /** - * @param session the session of the Bedrock client. * @return true if this entity is currently in air. */ - protected boolean isInAir(GeyserSession session) { + protected boolean isInAir() { int block = session.getConnector().getWorldManager().getBlockAt(session, position.toInt()); return block == BlockStateValues.JAVA_AIR_ID; } @Override - protected float getDrag(GeyserSession session) { + protected float getDrag() { return 0.92f; } } diff --git a/connector/src/main/java/org/geysermc/connector/entity/FurnaceMinecartEntity.java b/connector/src/main/java/org/geysermc/connector/entity/FurnaceMinecartEntity.java index fea1e62bd..d6791a15b 100644 --- a/connector/src/main/java/org/geysermc/connector/entity/FurnaceMinecartEntity.java +++ b/connector/src/main/java/org/geysermc/connector/entity/FurnaceMinecartEntity.java @@ -26,33 +26,29 @@ package org.geysermc.connector.entity; import com.github.steveice10.mc.protocol.data.game.entity.metadata.EntityMetadata; +import com.github.steveice10.mc.protocol.data.game.entity.metadata.type.BooleanEntityMetadata; import com.nukkitx.math.vector.Vector3f; import com.nukkitx.protocol.bedrock.data.entity.EntityData; -import org.geysermc.connector.entity.type.EntityType; import org.geysermc.connector.network.session.GeyserSession; import org.geysermc.connector.network.translators.world.block.BlockStateValues; -public class FurnaceMinecartEntity extends DefaultBlockMinecartEntity { +import java.util.UUID; +public class FurnaceMinecartEntity extends DefaultBlockMinecartEntity { private boolean hasFuel = false; - public FurnaceMinecartEntity(long entityId, long geyserId, EntityType entityType, Vector3f position, Vector3f motion, Vector3f rotation) { - super(entityId, geyserId, entityType, position, motion, rotation); + public FurnaceMinecartEntity(GeyserSession session, long 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); + } + + public void setHasFuel(EntityMetadata entityMetadata) { + hasFuel = ((BooleanEntityMetadata) entityMetadata).getPrimitiveValue(); + updateDefaultBlockMetadata(); } @Override - public void updateBedrockMetadata(EntityMetadata entityMetadata, GeyserSession session) { - if (entityMetadata.getId() == 14 && !showCustomBlock) { - hasFuel = (boolean) entityMetadata.getValue(); - updateDefaultBlockMetadata(session); - } - - super.updateBedrockMetadata(entityMetadata, session); - } - - @Override - public void updateDefaultBlockMetadata(GeyserSession session) { - metadata.put(EntityData.DISPLAY_ITEM, session.getBlockMappings().getBedrockBlockId(hasFuel ? BlockStateValues.JAVA_FURNACE_LIT_ID : BlockStateValues.JAVA_FURNACE_ID)); - metadata.put(EntityData.DISPLAY_OFFSET, 6); + public void updateDefaultBlockMetadata() { + dirtyMetadata.put(EntityData.DISPLAY_ITEM, session.getBlockMappings().getBedrockBlockId(hasFuel ? BlockStateValues.JAVA_FURNACE_LIT_ID : BlockStateValues.JAVA_FURNACE_ID)); + dirtyMetadata.put(EntityData.DISPLAY_OFFSET, 6); } } diff --git a/connector/src/main/java/org/geysermc/connector/entity/ItemEntity.java b/connector/src/main/java/org/geysermc/connector/entity/ItemEntity.java index ba354c525..ab5e2906e 100644 --- a/connector/src/main/java/org/geysermc/connector/entity/ItemEntity.java +++ b/connector/src/main/java/org/geysermc/connector/entity/ItemEntity.java @@ -34,23 +34,24 @@ import com.nukkitx.protocol.bedrock.data.entity.EntityFlag; import com.nukkitx.protocol.bedrock.data.inventory.ItemData; import com.nukkitx.protocol.bedrock.packet.AddItemEntityPacket; import com.nukkitx.protocol.bedrock.packet.EntityEventPacket; -import org.geysermc.connector.entity.type.EntityType; import org.geysermc.connector.network.session.GeyserSession; import org.geysermc.connector.network.translators.item.ItemTranslator; import org.geysermc.connector.network.translators.world.block.BlockStateValues; +import java.util.UUID; + public class ItemEntity extends ThrowableEntity { protected ItemData item; private int waterLevel = -1; - public ItemEntity(long entityId, long geyserId, EntityType entityType, Vector3f position, Vector3f motion, Vector3f rotation) { - super(entityId, geyserId, entityType, position, motion, rotation); + public ItemEntity(GeyserSession session, long 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 - public void spawnEntity(GeyserSession session) { + public void spawnEntity() { if (item == null) { return; } @@ -58,36 +59,59 @@ public class ItemEntity extends ThrowableEntity { AddItemEntityPacket itemPacket = new AddItemEntityPacket(); itemPacket.setRuntimeEntityId(geyserId); itemPacket.setUniqueEntityId(geyserId); - itemPacket.setPosition(position.add(0d, this.entityType.getOffset(), 0d)); + itemPacket.setPosition(position.add(0d, this.definition.offset(), 0d)); itemPacket.setMotion(motion); itemPacket.setFromFishing(false); itemPacket.setItemInHand(item); - itemPacket.getMetadata().putAll(metadata); + itemPacket.getMetadata().putAll(dirtyMetadata); session.sendUpstreamPacket(itemPacket); } @Override - public void tick(GeyserSession session) { - if (isInWater(session)) { + public void tick() { + if (isInWater()) { return; } if (!onGround || (motion.getX() * motion.getX() + motion.getZ() * motion.getZ()) > 0.00001) { - float gravity = getGravity(session); + float gravity = getGravity(); motion = motion.down(gravity); - moveAbsoluteImmediate(session, position.add(motion), rotation, onGround, false); - float drag = getDrag(session); + moveAbsoluteImmediate(position.add(motion), yaw, pitch, headYaw, onGround, false); + float drag = getDrag(); motion = motion.mul(drag, 0.98f, drag); } } + public void setItem(EntityMetadata entityMetadata) { + ItemData item = ItemTranslator.translateToBedrock(session, entityMetadata.getValue()); + if (this.item == null) { + this.item = item; + spawnEntity(); + } else if (item.equals(this.item, false, true, true)) { + // Don't bother respawning the entity if items are equal + if (this.item.getCount() != item.getCount()) { + // Just item count updated; let's make this easy + this.item = item; + EntityEventPacket packet = new EntityEventPacket(); + packet.setRuntimeEntityId(geyserId); + packet.setType(EntityEventType.UPDATE_ITEM_STACK_SIZE); + packet.setData(this.item.getCount()); + session.sendUpstreamPacket(packet); + } + } else { + this.item = item; + despawnEntity(); + spawnEntity(); + } + } + @Override - protected void moveAbsoluteImmediate(GeyserSession session, Vector3f position, Vector3f rotation, boolean isOnGround, boolean teleported) { - float offset = entityType.getOffset(); + protected void moveAbsoluteImmediate(Vector3f position, float yaw, float pitch, float headYaw, boolean isOnGround, boolean teleported) { + float offset = definition.offset(); if (waterLevel == 0) { // Item is in a full block of water // Move the item entity down so it doesn't float above the water - offset = -entityType.getOffset(); + offset = -definition.offset(); } - super.moveAbsoluteImmediate(session, position.add(0, offset, 0), Vector3f.ZERO, isOnGround, teleported); + super.moveAbsoluteImmediate(position.add(0, offset, 0), 0, 0, 0, isOnGround, teleported); this.position = position; int block = session.getConnector().getWorldManager().getBlockAt(session, position.toInt()); @@ -95,8 +119,8 @@ public class ItemEntity extends ThrowableEntity { } @Override - protected float getGravity(GeyserSession session) { - if (metadata.getFlags().getFlag(EntityFlag.HAS_GRAVITY) && !onGround && !isInWater(session)) { + protected float getGravity() { + if (getFlag(EntityFlag.HAS_GRAVITY) && !onGround && !isInWater()) { // Gravity can change if the item is in water/lava, but // the server calculates the motion & position for us return 0.04f; @@ -105,7 +129,7 @@ public class ItemEntity extends ThrowableEntity { } @Override - protected float getDrag(GeyserSession session) { + protected float getDrag() { if (onGround) { Vector3i groundBlockPos = position.toInt().down(1); int blockState = session.getConnector().getWorldManager().getBlockAt(session, groundBlockPos); @@ -115,35 +139,7 @@ public class ItemEntity extends ThrowableEntity { } @Override - public void updateBedrockMetadata(EntityMetadata entityMetadata, GeyserSession session) { - if (entityMetadata.getId() == 8) { - ItemData item = ItemTranslator.translateToBedrock(session, (ItemStack) entityMetadata.getValue()); - if (this.item == null) { - this.item = item; - spawnEntity(session); - } else if (item.equals(this.item, false, true, true)) { - // Don't bother respawning the entity if items are equal - if (this.item.getCount() != item.getCount()) { - // Just item count updated; let's make this easy - this.item = item; - EntityEventPacket packet = new EntityEventPacket(); - packet.setRuntimeEntityId(geyserId); - packet.setType(EntityEventType.UPDATE_ITEM_STACK_SIZE); - packet.setData(this.item.getCount()); - session.sendUpstreamPacket(packet); - } - } else { - this.item = item; - despawnEntity(session); - spawnEntity(session); - } - } - - super.updateBedrockMetadata(entityMetadata, session); - } - - @Override - protected boolean isInWater(GeyserSession session) { + protected boolean isInWater() { return waterLevel != -1; } } diff --git a/connector/src/main/java/org/geysermc/connector/entity/ItemFrameEntity.java b/connector/src/main/java/org/geysermc/connector/entity/ItemFrameEntity.java index 6411a4f55..5730fdaff 100644 --- a/connector/src/main/java/org/geysermc/connector/entity/ItemFrameEntity.java +++ b/connector/src/main/java/org/geysermc/connector/entity/ItemFrameEntity.java @@ -27,7 +27,9 @@ package org.geysermc.connector.entity; import com.github.steveice10.mc.protocol.data.game.entity.metadata.EntityMetadata; import com.github.steveice10.mc.protocol.data.game.entity.metadata.ItemStack; -import com.github.steveice10.mc.protocol.data.game.entity.object.HangingDirection; +import com.github.steveice10.mc.protocol.data.game.entity.metadata.type.IntEntityMetadata; +import com.github.steveice10.mc.protocol.data.game.entity.object.Direction; +import com.github.steveice10.mc.protocol.data.game.entity.type.EntityType; import com.nukkitx.math.vector.Vector3f; import com.nukkitx.math.vector.Vector3i; import com.nukkitx.nbt.NbtMap; @@ -37,29 +39,25 @@ import com.nukkitx.protocol.bedrock.packet.BlockEntityDataPacket; import com.nukkitx.protocol.bedrock.packet.UpdateBlockPacket; import com.nukkitx.protocol.bedrock.v465.Bedrock_v465; import lombok.Getter; -import org.geysermc.connector.entity.type.EntityType; import org.geysermc.connector.network.session.GeyserSession; import org.geysermc.connector.network.translators.item.ItemTranslator; import org.geysermc.connector.registry.type.ItemMapping; +import java.util.UUID; + /** * Item frames are an entity in Java but a block entity in Bedrock. */ public class ItemFrameEntity extends Entity { - - /** - * Used to construct the block entity tag on spawning. - */ - private final HangingDirection direction; /** * Used for getting the Bedrock block position. * Blocks deal with integers whereas entities deal with floats. */ - private Vector3i bedrockPosition; + private final Vector3i bedrockPosition; /** * Specific block 'state' we are emulating in Bedrock. */ - private int bedrockRuntimeId; + private final int bedrockRuntimeId; /** * Rotation of item in frame. */ @@ -73,16 +71,16 @@ public class ItemFrameEntity extends Entity { */ @Getter private ItemStack heldItem = null; + /** + * Determines if this entity needs updated on the client end/ + */ + private boolean changed = true; - public ItemFrameEntity(long entityId, long geyserId, EntityType entityType, Vector3f position, Vector3f motion, Vector3f rotation, HangingDirection direction) { - super(entityId, geyserId, entityType, position, motion, rotation); - this.direction = direction; - } + public ItemFrameEntity(GeyserSession session, long entityId, long geyserId, UUID uuid, EntityDefinition definition, Vector3f position, Vector3f motion, float yaw, float pitch, Direction direction) { + super(session, entityId, geyserId, uuid, definition, position, motion, yaw, pitch, 0f); - @Override - public void spawnEntity(GeyserSession session) { NbtMapBuilder blockBuilder = NbtMap.builder() - .putString("name", this.entityType == EntityType.GLOW_ITEM_FRAME ? "minecraft:glow_frame" : "minecraft:frame") + .putString("name", this.definition.entityType() == EntityType.GLOW_ITEM_FRAME ? "minecraft:glow_frame" : "minecraft:frame") .putInt("version", session.getBlockMappings().getBlockStateVersion()); NbtMapBuilder statesBuilder = NbtMap.builder() .putInt("facing_direction", direction.ordinal()) @@ -96,18 +94,26 @@ public class ItemFrameEntity extends Entity { bedrockPosition = Vector3i.from(position.getFloorX(), position.getFloorY(), position.getFloorZ()); session.getItemFrameCache().put(bedrockPosition, this); + } - updateBlock(session); + @Override + protected void initializeMetadata() { + // lol nah don't do anything + // This isn't a real entity for Bedrock so it isn't going to do anything + } + + @Override + public void spawnEntity() { + updateBlock(); session.getConnector().getLogger().debug("Spawned item frame at location " + bedrockPosition + " with java id " + entityId); valid = true; } - @Override - public void updateBedrockMetadata(EntityMetadata entityMetadata, GeyserSession session) { - if (entityMetadata.getId() == 8 && entityMetadata.getValue() != null) { - this.heldItem = (ItemStack) entityMetadata.getValue(); + public void setItemInFrame(EntityMetadata entityMetadata) { + if (entityMetadata.getValue() != null) { + this.heldItem = entityMetadata.getValue(); ItemData itemData = ItemTranslator.translateToBedrock(session, heldItem); - ItemMapping mapping = session.getItemMappings().getMapping((ItemStack) entityMetadata.getValue()); + ItemMapping mapping = session.getItemMappings().getMapping(entityMetadata.getValue()); NbtMapBuilder builder = NbtMap.builder(); builder.putByte("Count", (byte) itemData.getCount()); @@ -121,34 +127,30 @@ public class ItemFrameEntity extends Entity { tag.putFloat("ItemDropChance", 1.0f); tag.putFloat("ItemRotation", rotation); cachedTag = tag.build(); - updateBlock(session); - } - else if (entityMetadata.getId() == 8 && entityMetadata.getValue() == null && cachedTag != null) { + changed = true; + } else if (cachedTag != null) { cachedTag = getDefaultTag(); - updateBlock(session); - } - else if (entityMetadata.getId() == 9) { - rotation = ((int) entityMetadata.getValue()) * 45; - if (cachedTag == null) { - updateBlock(session); - return; - } - NbtMapBuilder builder = cachedTag.toBuilder(); - builder.putFloat("ItemRotation", rotation); - cachedTag = builder.build(); - updateBlock(session); - } - else { - updateBlock(session); + changed = true; } } + public void setItemRotation(EntityMetadata entityMetadata) { + rotation = ((IntEntityMetadata) entityMetadata).getPrimitiveValue() * 45; + if (cachedTag == null) { + return; + } + NbtMapBuilder builder = cachedTag.toBuilder(); + builder.putFloat("ItemRotation", rotation); + cachedTag = builder.build(); + changed = true; + } + @Override - public boolean despawnEntity(GeyserSession session) { + public boolean despawnEntity() { UpdateBlockPacket updateBlockPacket = new UpdateBlockPacket(); updateBlockPacket.setDataLayer(0); updateBlockPacket.setBlockPosition(bedrockPosition); - updateBlockPacket.setRuntimeId(session.getBlockMappings().getBedrockAirId()); + updateBlockPacket.setRuntimeId(session.getBlockMappings().getBedrockAirId()); //TODO maybe set this to the world block or another item frame? updateBlockPacket.getFlags().add(UpdateBlockPacket.Flag.PRIORITY); updateBlockPacket.getFlags().add(UpdateBlockPacket.Flag.NETWORK); updateBlockPacket.getFlags().add(UpdateBlockPacket.Flag.NEIGHBORS); @@ -166,15 +168,23 @@ public class ItemFrameEntity extends Entity { builder.putInt("y", bedrockPosition.getY()); builder.putInt("z", bedrockPosition.getZ()); builder.putByte("isMovable", (byte) 1); - builder.putString("id", this.entityType == EntityType.GLOW_ITEM_FRAME ? "GlowItemFrame" : "ItemFrame"); + builder.putString("id", this.definition.entityType() == EntityType.GLOW_ITEM_FRAME ? "GlowItemFrame" : "ItemFrame"); return builder.build(); } + @Override + public void updateBedrockMetadata() { + updateBlock(); + } + /** * Updates the item frame as a block - * @param session GeyserSession. */ - public void updateBlock(GeyserSession session) { + public void updateBlock() { + if (!changed) { + // Don't send a block update packet - nothing changed + return; + } UpdateBlockPacket updateBlockPacket = new UpdateBlockPacket(); updateBlockPacket.setDataLayer(0); updateBlockPacket.setBlockPosition(bedrockPosition); @@ -193,6 +203,8 @@ public class ItemFrameEntity extends Entity { } session.sendUpstreamPacket(blockEntityDataPacket); + + changed = false; } /** diff --git a/connector/src/main/java/org/geysermc/connector/entity/ItemedFireballEntity.java b/connector/src/main/java/org/geysermc/connector/entity/ItemedFireballEntity.java index 58a3b6f6c..16f361683 100644 --- a/connector/src/main/java/org/geysermc/connector/entity/ItemedFireballEntity.java +++ b/connector/src/main/java/org/geysermc/connector/entity/ItemedFireballEntity.java @@ -26,9 +26,10 @@ package org.geysermc.connector.entity; import com.nukkitx.math.vector.Vector3f; -import org.geysermc.connector.entity.type.EntityType; import org.geysermc.connector.network.session.GeyserSession; +import java.util.UUID; + public class ItemedFireballEntity extends ThrowableEntity { private final Vector3f acceleration; @@ -37,8 +38,8 @@ public class ItemedFireballEntity extends ThrowableEntity { */ protected int futureTicks = 3; - public ItemedFireballEntity(long entityId, long geyserId, EntityType entityType, Vector3f position, Vector3f motion, Vector3f rotation) { - super(entityId, geyserId, entityType, position, Vector3f.ZERO, rotation); + public ItemedFireballEntity(GeyserSession session, long entityId, long geyserId, UUID uuid, EntityDefinition definition, Vector3f position, Vector3f motion, float yaw, float pitch, float headYaw) { + super(session, entityId, geyserId, uuid, definition, position, Vector3f.ZERO, yaw, pitch, headYaw); float magnitude = motion.length(); if (magnitude != 0) { @@ -48,28 +49,28 @@ public class ItemedFireballEntity extends ThrowableEntity { } } - private Vector3f tickMovement(GeyserSession session, Vector3f position) { + private Vector3f tickMovement(Vector3f position) { position = position.add(motion); - float drag = getDrag(session); + float drag = getDrag(); motion = motion.add(acceleration).mul(drag); return position; } @Override - protected void moveAbsoluteImmediate(GeyserSession session, Vector3f position, Vector3f rotation, boolean isOnGround, boolean teleported) { + protected void moveAbsoluteImmediate(Vector3f position, float yaw, float pitch, float headYaw, boolean isOnGround, boolean teleported) { // Advance the position by a few ticks before sending it to Bedrock Vector3f lastMotion = motion; Vector3f newPosition = position; for (int i = 0; i < futureTicks; i++) { - newPosition = tickMovement(session, newPosition); + newPosition = tickMovement(newPosition); } - super.moveAbsoluteImmediate(session, newPosition, rotation, isOnGround, teleported); + super.moveAbsoluteImmediate(newPosition, yaw, pitch, headYaw, isOnGround, teleported); this.position = position; this.motion = lastMotion; } @Override - public void tick(GeyserSession session) { - moveAbsoluteImmediate(session, tickMovement(session, position), rotation, false, false); + public void tick() { + moveAbsoluteImmediate(tickMovement(position), yaw, pitch, headYaw, false, false); } } diff --git a/connector/src/main/java/org/geysermc/connector/entity/LeashKnotEntity.java b/connector/src/main/java/org/geysermc/connector/entity/LeashKnotEntity.java index a10a30a8d..c5697346e 100644 --- a/connector/src/main/java/org/geysermc/connector/entity/LeashKnotEntity.java +++ b/connector/src/main/java/org/geysermc/connector/entity/LeashKnotEntity.java @@ -26,13 +26,15 @@ package org.geysermc.connector.entity; import com.nukkitx.math.vector.Vector3f; -import org.geysermc.connector.entity.type.EntityType; +import org.geysermc.connector.network.session.GeyserSession; + +import java.util.UUID; public class LeashKnotEntity extends Entity { - public LeashKnotEntity(long entityId, long geyserId, EntityType entityType, Vector3f position, Vector3f motion, Vector3f rotation) { + public LeashKnotEntity(GeyserSession session, long entityId, long geyserId, UUID uuid, EntityDefinition definition, Vector3f position, Vector3f motion, float yaw, float pitch, float headYaw) { // Position is incorrect by default - super(entityId, geyserId, entityType, position.add(0.5f, 0.25f, 0.5f), motion, rotation); + super(session, entityId, geyserId, uuid, definition, position.add(0.5f, 0.25f, 0.5f), motion, yaw, pitch, headYaw); } } diff --git a/connector/src/main/java/org/geysermc/connector/entity/LightningEntity.java b/connector/src/main/java/org/geysermc/connector/entity/LightningEntity.java index 2255519e5..d9c5655b3 100644 --- a/connector/src/main/java/org/geysermc/connector/entity/LightningEntity.java +++ b/connector/src/main/java/org/geysermc/connector/entity/LightningEntity.java @@ -27,20 +27,20 @@ package org.geysermc.connector.entity; import com.nukkitx.math.vector.Vector3f; import com.nukkitx.protocol.bedrock.packet.PlaySoundPacket; -import org.geysermc.connector.entity.type.EntityType; import org.geysermc.connector.network.session.GeyserSession; +import java.util.UUID; import java.util.concurrent.ThreadLocalRandom; public class LightningEntity extends Entity { - public LightningEntity(long entityId, long geyserId, EntityType entityType, Vector3f position, Vector3f motion, Vector3f rotation) { - super(entityId, geyserId, entityType, position, motion, rotation); + public LightningEntity(GeyserSession session, long 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 - public void spawnEntity(GeyserSession session) { - super.spawnEntity(session); + public void spawnEntity() { + super.spawnEntity(); // Add these two sound effects - they're done completely clientside on Java Edition as of 1.17.1 ThreadLocalRandom random = ThreadLocalRandom.current(); diff --git a/connector/src/main/java/org/geysermc/connector/entity/LivingEntity.java b/connector/src/main/java/org/geysermc/connector/entity/LivingEntity.java index 44a9859d5..28f97a2a3 100644 --- a/connector/src/main/java/org/geysermc/connector/entity/LivingEntity.java +++ b/connector/src/main/java/org/geysermc/connector/entity/LivingEntity.java @@ -29,6 +29,8 @@ 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; +import com.github.steveice10.mc.protocol.data.game.entity.metadata.type.ByteEntityMetadata; +import com.github.steveice10.mc.protocol.data.game.entity.metadata.type.FloatEntityMetadata; import com.nukkitx.math.vector.Vector3f; import com.nukkitx.math.vector.Vector3i; import com.nukkitx.protocol.bedrock.data.AttributeData; @@ -43,7 +45,6 @@ import lombok.AccessLevel; import lombok.Getter; import lombok.Setter; 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.registry.type.ItemMapping; import org.geysermc.connector.utils.AttributeUtils; @@ -52,6 +53,7 @@ import org.geysermc.connector.utils.ChunkUtils; import java.util.ArrayList; import java.util.Collections; import java.util.List; +import java.util.UUID; @Getter @Setter @@ -74,82 +76,87 @@ public class LivingEntity extends Entity { */ private boolean isMaxFrozenState = false; - public LivingEntity(long entityId, long geyserId, EntityType entityType, Vector3f position, Vector3f motion, Vector3f rotation) { - super(entityId, geyserId, entityType, position, motion, rotation); + public LivingEntity(GeyserSession session, long 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(); // Matches Bedrock behavior; is always set to this - metadata.put(EntityData.HEALTH, 1); + dirtyMetadata.put(EntityData.HEALTH, 1); } - @Override - public void updateBedrockMetadata(EntityMetadata entityMetadata, GeyserSession session) { - switch (entityMetadata.getId()) { - case 8 -> { // blocking - byte xd = (byte) entityMetadata.getValue(); + public void setLivingEntityFlags(EntityMetadata entityMetadata) { + byte xd = ((ByteEntityMetadata) entityMetadata).getPrimitiveValue(); - //blocking gets triggered when using a bow, but if we set USING_ITEM for all items, it may look like - //you're "mining" with ex. a shield. - ItemMapping shield = session.getItemMappings().getStoredItems().shield(); - boolean isUsingShield = (getHand().getId() == shield.getBedrockId() || - getHand().equals(ItemData.AIR) && getOffHand().getId() == shield.getBedrockId()); - metadata.getFlags().setFlag(EntityFlag.USING_ITEM, (xd & 0x01) == 0x01 && !isUsingShield); - metadata.getFlags().setFlag(EntityFlag.BLOCKING, (xd & 0x01) == 0x01); + // Blocking gets triggered when using a bow, but if we set USING_ITEM for all items, it may look like + // you're "mining" with ex. a shield. + ItemMapping shield = session.getItemMappings().getStoredItems().shield(); + boolean isUsingShield = (getHand().getId() == shield.getBedrockId() || + getHand().equals(ItemData.AIR) && getOffHand().getId() == shield.getBedrockId()); + setFlag(EntityFlag.USING_ITEM, (xd & 0x01) == 0x01 && !isUsingShield); + setFlag(EntityFlag.BLOCKING, (xd & 0x01) == 0x01); - // Riptide spin attack - metadata.getFlags().setFlag(EntityFlag.DAMAGE_NEARBY_MOBS, (xd & 0x04) == 0x04); - } - case 9 -> { - this.health = (float) entityMetadata.getValue(); + // Riptide spin attack + setFlag(EntityFlag.DAMAGE_NEARBY_MOBS, (xd & 0x04) == 0x04); + } - AttributeData healthData = createHealthAttribute(); - UpdateAttributesPacket attributesPacket = new UpdateAttributesPacket(); - attributesPacket.setRuntimeEntityId(geyserId); - attributesPacket.setAttributes(Collections.singletonList(healthData)); - session.sendUpstreamPacket(attributesPacket); - } - case 10 -> metadata.put(EntityData.EFFECT_COLOR, entityMetadata.getValue()); - case 11 -> metadata.put(EntityData.EFFECT_AMBIENT, (byte) ((boolean) entityMetadata.getValue() ? 1 : 0)); - case 14 -> { // Bed Position - Position bedPosition = (Position) entityMetadata.getValue(); - if (bedPosition != null) { - metadata.put(EntityData.BED_POSITION, Vector3i.from(bedPosition.getX(), bedPosition.getY(), bedPosition.getZ())); - int bed = session.getConnector().getWorldManager().getBlockAt(session, bedPosition); - // Bed has to be updated, or else player is floating in the air - ChunkUtils.updateBlock(session, bed, bedPosition); - // Indicate that the player should enter the sleep cycle - // Has to be a byte or it does not work - // (Bed position is what actually triggers sleep - "pose" is only optional) - metadata.put(EntityData.PLAYER_FLAGS, (byte) 2); - } else { - // Player is no longer sleeping - metadata.put(EntityData.PLAYER_FLAGS, (byte) 0); - } - } + public void setHealth(EntityMetadata entityMetadata) { + this.health = ((FloatEntityMetadata) entityMetadata).getPrimitiveValue(); + + AttributeData healthData = createHealthAttribute(); + UpdateAttributesPacket attributesPacket = new UpdateAttributesPacket(); + attributesPacket.setRuntimeEntityId(geyserId); + attributesPacket.setAttributes(Collections.singletonList(healthData)); + session.sendUpstreamPacket(attributesPacket); + } + + public Vector3i setBedPosition(EntityMetadata entityMetadata) { + Position bedPosition = entityMetadata.getValue(); + if (bedPosition != null) { + Vector3i vector = Vector3i.from(bedPosition.getX(), bedPosition.getY(), bedPosition.getZ()); + dirtyMetadata.put(EntityData.BED_POSITION, vector); + int bed = session.getConnector().getWorldManager().getBlockAt(session, bedPosition); + // Bed has to be updated, or else player is floating in the air + ChunkUtils.updateBlock(session, bed, bedPosition); + // Indicate that the player should enter the sleep cycle + // Has to be a byte or it does not work + // (Bed position is what actually triggers sleep - "pose" is only optional) + dirtyMetadata.put(EntityData.PLAYER_FLAGS, (byte) 2); + return vector; + } else { + // Player is no longer sleeping + dirtyMetadata.put(EntityData.PLAYER_FLAGS, (byte) 0); + return null; } - - super.updateBedrockMetadata(entityMetadata, session); } @Override - protected boolean isShaking(GeyserSession session) { + protected boolean isShaking() { return isMaxFrozenState; } @Override protected void setDimensions(Pose pose) { if (pose == Pose.SLEEPING) { - metadata.put(EntityData.BOUNDING_BOX_WIDTH, 0.2f); - metadata.put(EntityData.BOUNDING_BOX_HEIGHT, 0.2f); + boundingBoxWidth = 0.2f; + boundingBoxHeight = 0.2f; + if (boundingBoxWidth != definition.width() || boundingBoxHeight != definition.height()) { + dirtyMetadata.put(EntityData.BOUNDING_BOX_WIDTH, boundingBoxWidth); + dirtyMetadata.put(EntityData.BOUNDING_BOX_HEIGHT, boundingBoxHeight); + } } else { super.setDimensions(pose); } } @Override - protected void setFreezing(GeyserSession session, float amount) { - super.setFreezing(session, amount); - this.isMaxFrozenState = amount >= 1.0f; - metadata.getFlags().setFlag(EntityFlag.SHAKING, isShaking(session)); + public float setFreezing(EntityMetadata entityMetadata) { + float freezingPercentage = super.setFreezing(entityMetadata); + this.isMaxFrozenState = freezingPercentage >= 1.0f; + setFlag(EntityFlag.SHAKING, isShaking()); + return freezingPercentage; } /** diff --git a/connector/src/main/java/org/geysermc/connector/entity/MinecartEntity.java b/connector/src/main/java/org/geysermc/connector/entity/MinecartEntity.java index 6ab9a1b22..5b514310d 100644 --- a/connector/src/main/java/org/geysermc/connector/entity/MinecartEntity.java +++ b/connector/src/main/java/org/geysermc/connector/entity/MinecartEntity.java @@ -26,63 +26,42 @@ package org.geysermc.connector.entity; import com.github.steveice10.mc.protocol.data.game.entity.metadata.EntityMetadata; +import com.github.steveice10.mc.protocol.data.game.entity.metadata.type.BooleanEntityMetadata; +import com.github.steveice10.mc.protocol.data.game.entity.metadata.type.IntEntityMetadata; import com.nukkitx.math.vector.Vector3f; import com.nukkitx.protocol.bedrock.data.entity.EntityData; -import org.geysermc.connector.entity.type.EntityType; import org.geysermc.connector.network.session.GeyserSession; +import java.util.UUID; + public class MinecartEntity extends Entity { - public MinecartEntity(long entityId, long geyserId, EntityType entityType, Vector3f position, Vector3f motion, Vector3f rotation) { - super(entityId, geyserId, entityType, position.add(0d, entityType.getOffset(), 0d), motion, rotation); + public MinecartEntity(GeyserSession session, long entityId, long geyserId, UUID uuid, EntityDefinition definition, Vector3f position, Vector3f motion, float yaw, float pitch, float headYaw) { + super(session, entityId, geyserId, uuid, definition, position.add(0d, definition.offset(), 0d), motion, yaw, pitch, headYaw); + } + + public void setCustomBlock(EntityMetadata entityMetadata) { + dirtyMetadata.put(EntityData.DISPLAY_ITEM, session.getBlockMappings().getBedrockBlockId(((IntEntityMetadata) entityMetadata).getPrimitiveValue())); + } + + public void setCustomBlockOffset(EntityMetadata entityMetadata) { + dirtyMetadata.put(EntityData.DISPLAY_OFFSET, entityMetadata.getValue()); + } + + public void setShowCustomBlock(EntityMetadata entityMetadata) { + // If the custom block should be enabled + // Needs a byte based off of Java's boolean + dirtyMetadata.put(EntityData.CUSTOM_DISPLAY, (byte) (((BooleanEntityMetadata) entityMetadata).getPrimitiveValue() ? 1 : 0)); } @Override - public void updateBedrockMetadata(EntityMetadata entityMetadata, GeyserSession session) { - - if (entityMetadata.getId() == 8) { - metadata.put(EntityData.HEALTH, entityMetadata.getValue()); - } - - // Direction in which the minecart is shaking - if (entityMetadata.getId() == 9) { - metadata.put(EntityData.HURT_DIRECTION, entityMetadata.getValue()); - } - - // Power in Java, time in Bedrock - if (entityMetadata.getId() == 10) { - metadata.put(EntityData.HURT_TIME, Math.min((int) (float) entityMetadata.getValue(), 15)); - } - - if (!(this instanceof DefaultBlockMinecartEntity)) { // Handled in the DefaultBlockMinecartEntity class - // Custom block - if (entityMetadata.getId() == 11) { - metadata.put(EntityData.DISPLAY_ITEM, session.getBlockMappings().getBedrockBlockId((int) entityMetadata.getValue())); - } - - // Custom block offset - if (entityMetadata.getId() == 12) { - metadata.put(EntityData.DISPLAY_OFFSET, entityMetadata.getValue()); - } - - // If the custom block should be enabled - if (entityMetadata.getId() == 13) { - // Needs a byte based off of Java's boolean - metadata.put(EntityData.CUSTOM_DISPLAY, (byte) ((boolean) entityMetadata.getValue() ? 1 : 0)); - } - } - - super.updateBedrockMetadata(entityMetadata, session); - } - - @Override - public void moveAbsolute(GeyserSession session, Vector3f position, Vector3f rotation, boolean isOnGround, boolean teleported) { - super.moveAbsolute(session, position.add(0d, this.entityType.getOffset(), 0d), rotation, isOnGround, teleported); + public void moveAbsolute(Vector3f position, float yaw, float pitch, float headYaw, boolean isOnGround, boolean teleported) { + super.moveAbsolute(position.add(0d, this.definition.offset(), 0d), yaw, pitch, headYaw, isOnGround, teleported); } @Override public Vector3f getBedrockRotation() { // Note: minecart rotation on rails does not care about the actual rotation value - return Vector3f.from(0, rotation.getX(), 0); + return Vector3f.from(0, yaw, 0); } } diff --git a/connector/src/main/java/org/geysermc/connector/entity/PaintingEntity.java b/connector/src/main/java/org/geysermc/connector/entity/PaintingEntity.java index b620e598b..e164883c5 100644 --- a/connector/src/main/java/org/geysermc/connector/entity/PaintingEntity.java +++ b/connector/src/main/java/org/geysermc/connector/entity/PaintingEntity.java @@ -27,23 +27,24 @@ package org.geysermc.connector.entity; import com.nukkitx.math.vector.Vector3f; import com.nukkitx.protocol.bedrock.packet.AddPaintingPacket; -import org.geysermc.connector.entity.type.EntityType; import org.geysermc.connector.network.session.GeyserSession; import org.geysermc.connector.utils.PaintingType; +import java.util.UUID; + public class PaintingEntity extends Entity { private static final double OFFSET = -0.46875; private final PaintingType paintingName; private final int direction; - public PaintingEntity(long entityId, long geyserId, Vector3f position, PaintingType paintingName, int direction) { - super(entityId, geyserId, EntityType.PAINTING, position, Vector3f.ZERO, Vector3f.ZERO); + public PaintingEntity(GeyserSession session, long entityId, long geyserId, UUID uuid, Vector3f position, PaintingType paintingName, int direction) { + super(session, entityId, geyserId, uuid, EntityDefinitions.PAINTING, position, Vector3f.ZERO, 0f, 0f, 0f); this.paintingName = paintingName; this.direction = direction; } @Override - public void spawnEntity(GeyserSession session) { + public void spawnEntity() { AddPaintingPacket addPaintingPacket = new AddPaintingPacket(); addPaintingPacket.setUniqueEntityId(geyserId); addPaintingPacket.setRuntimeEntityId(geyserId); @@ -58,7 +59,7 @@ public class PaintingEntity extends Entity { } @Override - public void updateHeadLookRotation(GeyserSession session, float headYaw) { + public void updateHeadLookRotation(float headYaw) { // Do nothing, as head look messes up paintings } diff --git a/connector/src/main/java/org/geysermc/connector/entity/SpawnerMinecartEntity.java b/connector/src/main/java/org/geysermc/connector/entity/SpawnerMinecartEntity.java index c6d4c3aa5..0cf159f0e 100644 --- a/connector/src/main/java/org/geysermc/connector/entity/SpawnerMinecartEntity.java +++ b/connector/src/main/java/org/geysermc/connector/entity/SpawnerMinecartEntity.java @@ -27,19 +27,20 @@ package org.geysermc.connector.entity; import com.nukkitx.math.vector.Vector3f; import com.nukkitx.protocol.bedrock.data.entity.EntityData; -import org.geysermc.connector.entity.type.EntityType; import org.geysermc.connector.network.session.GeyserSession; import org.geysermc.connector.network.translators.world.block.BlockStateValues; +import java.util.UUID; + public class SpawnerMinecartEntity extends DefaultBlockMinecartEntity { - public SpawnerMinecartEntity(long entityId, long geyserId, EntityType entityType, Vector3f position, Vector3f motion, Vector3f rotation) { - super(entityId, geyserId, entityType, position, motion, rotation); + public SpawnerMinecartEntity(GeyserSession session, long 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 - public void updateDefaultBlockMetadata(GeyserSession session) { - metadata.put(EntityData.DISPLAY_ITEM, session.getBlockMappings().getBedrockBlockId(BlockStateValues.JAVA_SPAWNER_ID)); - metadata.put(EntityData.DISPLAY_OFFSET, 6); + public void updateDefaultBlockMetadata() { + dirtyMetadata.put(EntityData.DISPLAY_ITEM, session.getBlockMappings().getBedrockBlockId(BlockStateValues.JAVA_SPAWNER_ID)); + dirtyMetadata.put(EntityData.DISPLAY_OFFSET, 6); } } diff --git a/connector/src/main/java/org/geysermc/connector/entity/TNTEntity.java b/connector/src/main/java/org/geysermc/connector/entity/TNTEntity.java index 08ffa0dbc..39482b16e 100644 --- a/connector/src/main/java/org/geysermc/connector/entity/TNTEntity.java +++ b/connector/src/main/java/org/geysermc/connector/entity/TNTEntity.java @@ -26,41 +26,37 @@ package org.geysermc.connector.entity; import com.github.steveice10.mc.protocol.data.game.entity.metadata.EntityMetadata; +import com.github.steveice10.mc.protocol.data.game.entity.metadata.type.IntEntityMetadata; import com.nukkitx.math.vector.Vector3f; import com.nukkitx.protocol.bedrock.data.entity.EntityData; import com.nukkitx.protocol.bedrock.data.entity.EntityFlag; import com.nukkitx.protocol.bedrock.packet.SetEntityDataPacket; -import org.geysermc.connector.entity.type.EntityType; import org.geysermc.connector.network.session.GeyserSession; -public class TNTEntity extends Entity implements Tickable { +import java.util.UUID; +public class TNTEntity extends Entity implements Tickable { private int currentTick; - public TNTEntity(long entityId, long geyserId, EntityType entityType, Vector3f position, Vector3f motion, Vector3f rotation) { - super(entityId, geyserId, entityType, position, motion, rotation); + public TNTEntity(GeyserSession session, long 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); + } + + public void setFuseLength(EntityMetadata entityMetadata) { + currentTick = ((IntEntityMetadata) entityMetadata).getPrimitiveValue(); + setFlag(EntityFlag.IGNITED, true); + dirtyMetadata.put(EntityData.FUSE_LENGTH, currentTick); } @Override - public void updateBedrockMetadata(EntityMetadata entityMetadata, GeyserSession session) { - if (entityMetadata.getId() == 8) { - currentTick = (int) entityMetadata.getValue(); - metadata.getFlags().setFlag(EntityFlag.IGNITED, true); - metadata.put(EntityData.FUSE_LENGTH, currentTick); - } - - super.updateBedrockMetadata(entityMetadata, session); - } - - @Override - public void tick(GeyserSession session) { + public void tick() { if (currentTick == 0) { // No need to update the fuse when there is none return; } if (currentTick % 5 == 0) { - metadata.put(EntityData.FUSE_LENGTH, currentTick); + dirtyMetadata.put(EntityData.FUSE_LENGTH, currentTick); SetEntityDataPacket packet = new SetEntityDataPacket(); packet.setRuntimeEntityId(geyserId); diff --git a/connector/src/main/java/org/geysermc/connector/entity/ThrowableEntity.java b/connector/src/main/java/org/geysermc/connector/entity/ThrowableEntity.java index 1ba18bfd7..5c181fe0f 100644 --- a/connector/src/main/java/org/geysermc/connector/entity/ThrowableEntity.java +++ b/connector/src/main/java/org/geysermc/connector/entity/ThrowableEntity.java @@ -25,15 +25,17 @@ package org.geysermc.connector.entity; +import com.github.steveice10.mc.protocol.data.game.entity.type.EntityType; import com.nukkitx.math.vector.Vector3f; import com.nukkitx.protocol.bedrock.data.LevelEventType; import com.nukkitx.protocol.bedrock.data.entity.EntityFlag; import com.nukkitx.protocol.bedrock.packet.LevelEventPacket; import com.nukkitx.protocol.bedrock.packet.MoveEntityDeltaPacket; -import org.geysermc.connector.entity.type.EntityType; import org.geysermc.connector.network.session.GeyserSession; import org.geysermc.connector.network.translators.world.block.BlockStateValues; +import java.util.UUID; + /** * Used as a class for any object-like entity that moves as a projectile */ @@ -41,8 +43,8 @@ public class ThrowableEntity extends Entity implements Tickable { protected Vector3f lastJavaPosition; - public ThrowableEntity(long entityId, long geyserId, EntityType entityType, Vector3f position, Vector3f motion, Vector3f rotation) { - super(entityId, geyserId, entityType, position, motion, rotation); + public ThrowableEntity(GeyserSession session, long 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); this.lastJavaPosition = position; } @@ -52,14 +54,14 @@ public class ThrowableEntity extends Entity implements Tickable { * Java clients assume the next positions of moving items. Bedrock needs to be explicitly told positions */ @Override - public void tick(GeyserSession session) { - moveAbsoluteImmediate(session, position.add(motion), rotation, onGround, false); - float drag = getDrag(session); - float gravity = getGravity(session); + public void tick() { + moveAbsoluteImmediate(position.add(motion), yaw, pitch, headYaw, onGround, false); + float drag = getDrag(); + float gravity = getGravity(); motion = motion.mul(drag).down(gravity); } - protected void moveAbsoluteImmediate(GeyserSession session, Vector3f position, Vector3f rotation, boolean isOnGround, boolean teleported) { + protected void moveAbsoluteImmediate(Vector3f position, float yaw, float pitch, float headYaw, boolean isOnGround, boolean teleported) { MoveEntityDeltaPacket moveEntityDeltaPacket = new MoveEntityDeltaPacket(); moveEntityDeltaPacket.setRuntimeEntityId(geyserId); @@ -86,19 +88,21 @@ public class ThrowableEntity extends Entity implements Tickable { } setPosition(position); - if (this.rotation.getX() != rotation.getX()) { + if (this.yaw != yaw) { moveEntityDeltaPacket.getFlags().add(MoveEntityDeltaPacket.Flag.HAS_YAW); - moveEntityDeltaPacket.setYaw(rotation.getX()); + moveEntityDeltaPacket.setYaw(yaw); + this.yaw = yaw; } - if (this.rotation.getY() != rotation.getY()) { + if (this.pitch != pitch) { moveEntityDeltaPacket.getFlags().add(MoveEntityDeltaPacket.Flag.HAS_PITCH); - moveEntityDeltaPacket.setPitch(rotation.getY()); + moveEntityDeltaPacket.setPitch(pitch); + this.pitch = pitch; } - if (this.rotation.getZ() != rotation.getZ()) { + if (this.headYaw != headYaw) { moveEntityDeltaPacket.getFlags().add(MoveEntityDeltaPacket.Flag.HAS_HEAD_YAW); - moveEntityDeltaPacket.setHeadYaw(rotation.getZ()); + moveEntityDeltaPacket.setHeadYaw(headYaw); + this.headYaw = headYaw; } - setRotation(rotation); if (!moveEntityDeltaPacket.getFlags().isEmpty()) { session.sendUpstreamPacket(moveEntityDeltaPacket); @@ -108,14 +112,12 @@ public class ThrowableEntity extends Entity implements Tickable { /** * Get the gravity of this entity type. Used for applying gravity while the entity is in motion. * - * @param session the session of the Bedrock client. * @return the amount of gravity to apply to this entity while in motion. */ - protected float getGravity(GeyserSession session) { - if (metadata.getFlags().getFlag(EntityFlag.HAS_GRAVITY)) { - switch (entityType) { + protected float getGravity() { + if (getFlag(EntityFlag.HAS_GRAVITY)) { + switch (definition.entityType()) { case THROWN_POTION: - case LINGERING_POTION: return 0.05f; case THROWN_EXP_BOTTLE: return 0.07f; @@ -134,16 +136,14 @@ public class ThrowableEntity extends Entity implements Tickable { } /** - * @param session the session of the Bedrock client. * @return the drag that should be multiplied to the entity's motion */ - protected float getDrag(GeyserSession session) { - if (isInWater(session)) { + protected float getDrag() { + if (isInWater()) { return 0.8f; } else { - switch (entityType) { + switch (definition.entityType()) { case THROWN_POTION: - case LINGERING_POTION: case THROWN_EXP_BOTTLE: case SNOWBALL: case THROWN_EGG: @@ -162,34 +162,33 @@ public class ThrowableEntity extends Entity implements Tickable { } /** - * @param session the session of the Bedrock client. * @return true if this entity is currently in water. */ - protected boolean isInWater(GeyserSession session) { + protected boolean isInWater() { int block = session.getConnector().getWorldManager().getBlockAt(session, position.toInt()); return BlockStateValues.getWaterLevel(block) != -1; } @Override - public boolean despawnEntity(GeyserSession session) { - if (entityType == EntityType.THROWN_ENDERPEARL) { + public boolean despawnEntity() { + if (definition.entityType() == EntityType.THROWN_ENDERPEARL) { LevelEventPacket particlePacket = new LevelEventPacket(); particlePacket.setType(LevelEventType.PARTICLE_TELEPORT); particlePacket.setPosition(position); session.sendUpstreamPacket(particlePacket); } - return super.despawnEntity(session); + return super.despawnEntity(); } @Override - public void moveRelative(GeyserSession session, double relX, double relY, double relZ, Vector3f rotation, boolean isOnGround) { - moveAbsoluteImmediate(session, lastJavaPosition.add(relX, relY, relZ), rotation, isOnGround, false); + public void moveRelative(double relX, double relY, double relZ, float yaw, float pitch, float headYaw, boolean isOnGround) { + moveAbsoluteImmediate(lastJavaPosition.add(relX, relY, relZ), yaw, pitch, headYaw, isOnGround, false); lastJavaPosition = position; } @Override - public void moveAbsolute(GeyserSession session, Vector3f position, Vector3f rotation, boolean isOnGround, boolean teleported) { - moveAbsoluteImmediate(session, position, rotation, isOnGround, teleported); + public void moveAbsolute(Vector3f position, float yaw, float pitch, float headYaw, boolean isOnGround, boolean teleported) { + moveAbsoluteImmediate(position, yaw, pitch, headYaw, isOnGround, teleported); lastJavaPosition = position; } } diff --git a/connector/src/main/java/org/geysermc/connector/entity/ThrowableItemEntity.java b/connector/src/main/java/org/geysermc/connector/entity/ThrowableItemEntity.java index 38a6204d4..d1800f716 100644 --- a/connector/src/main/java/org/geysermc/connector/entity/ThrowableItemEntity.java +++ b/connector/src/main/java/org/geysermc/connector/entity/ThrowableItemEntity.java @@ -27,9 +27,10 @@ package org.geysermc.connector.entity; import com.nukkitx.math.vector.Vector3f; import com.nukkitx.protocol.bedrock.data.entity.EntityFlag; -import org.geysermc.connector.entity.type.EntityType; import org.geysermc.connector.network.session.GeyserSession; +import java.util.UUID; + /** * Used as a class for any projectile entity that looks like an item */ @@ -40,37 +41,37 @@ public class ThrowableItemEntity extends ThrowableEntity { private int age; private boolean invisible; - public ThrowableItemEntity(long entityId, long geyserId, EntityType entityType, Vector3f position, Vector3f motion, Vector3f rotation) { - super(entityId, geyserId, entityType, position, motion, rotation); - metadata.getFlags().setFlag(EntityFlag.INVISIBLE, true); + public ThrowableItemEntity(GeyserSession session, long 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); + setFlag(EntityFlag.INVISIBLE, true); invisible = false; } - private void checkVisibility(GeyserSession session) { - if (invisible != metadata.getFlags().getFlag(EntityFlag.INVISIBLE)) { + private void checkVisibility() { + if (invisible != getFlag(EntityFlag.INVISIBLE)) { if (!invisible) { Vector3f playerPos = session.getPlayerEntity().getPosition(); // Prevent projectiles from blocking the player's screen if (age >= 4 || position.distanceSquared(playerPos) > 16) { - metadata.getFlags().setFlag(EntityFlag.INVISIBLE, false); - updateBedrockMetadata(session); + setFlag(EntityFlag.INVISIBLE, false); + updateBedrockMetadata(); } } else { - metadata.getFlags().setFlag(EntityFlag.INVISIBLE, true); - updateBedrockMetadata(session); + setFlag(EntityFlag.INVISIBLE, true); + updateBedrockMetadata(); } } age++; } @Override - public void tick(GeyserSession session) { - checkVisibility(session); - super.tick(session); + public void tick() { + checkVisibility(); + super.tick(); } @Override - protected void setInvisible(GeyserSession session, boolean value) { + protected void setInvisible(boolean value) { invisible = value; } } diff --git a/connector/src/main/java/org/geysermc/connector/entity/ThrownPotionEntity.java b/connector/src/main/java/org/geysermc/connector/entity/ThrownPotionEntity.java index 578a460b6..6f5b6a88e 100644 --- a/connector/src/main/java/org/geysermc/connector/entity/ThrownPotionEntity.java +++ b/connector/src/main/java/org/geysermc/connector/entity/ThrownPotionEntity.java @@ -27,50 +27,44 @@ package org.geysermc.connector.entity; import com.github.steveice10.mc.protocol.data.game.entity.metadata.EntityMetadata; import com.github.steveice10.mc.protocol.data.game.entity.metadata.ItemStack; -import com.github.steveice10.mc.protocol.data.game.entity.metadata.MetadataType; import com.github.steveice10.opennbt.tag.builtin.StringTag; import com.github.steveice10.opennbt.tag.builtin.Tag; import com.nukkitx.math.vector.Vector3f; import com.nukkitx.protocol.bedrock.data.entity.EntityData; import com.nukkitx.protocol.bedrock.data.entity.EntityFlag; import org.geysermc.connector.GeyserConnector; -import org.geysermc.connector.entity.type.EntityType; import org.geysermc.connector.network.session.GeyserSession; import org.geysermc.connector.network.translators.item.Potion; import org.geysermc.connector.registry.type.ItemMapping; import java.util.EnumSet; +import java.util.UUID; public class ThrownPotionEntity extends ThrowableItemEntity { private static final EnumSet NON_ENCHANTED_POTIONS = EnumSet.of(Potion.WATER, Potion.MUNDANE, Potion.THICK, Potion.AWKWARD); - public ThrownPotionEntity(long entityId, long geyserId, EntityType entityType, Vector3f position, Vector3f motion, Vector3f rotation) { - super(entityId, geyserId, entityType, position, motion, rotation); + public ThrownPotionEntity(GeyserSession session, long 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 - public void updateBedrockMetadata(EntityMetadata entityMetadata, GeyserSession session) { - if (entityMetadata.getId() == 8 && entityMetadata.getType() == MetadataType.ITEM) { - ItemStack itemStack = (ItemStack) entityMetadata.getValue(); - ItemMapping mapping = session.getItemMappings().getMapping(itemStack); - if (mapping.getJavaIdentifier().endsWith("potion") && itemStack.getNbt() != null) { - Tag potionTag = itemStack.getNbt().get("Potion"); - if (potionTag instanceof StringTag) { - Potion potion = Potion.getByJavaIdentifier(((StringTag) potionTag).getValue()); - if (potion != null) { - metadata.put(EntityData.POTION_AUX_VALUE, potion.getBedrockId()); - metadata.getFlags().setFlag(EntityFlag.ENCHANTED, !NON_ENCHANTED_POTIONS.contains(potion)); - } else { - metadata.put(EntityData.POTION_AUX_VALUE, 0); - GeyserConnector.getInstance().getLogger().debug("Unknown java potion: " + potionTag.getValue()); - } + public void setPotion(EntityMetadata entityMetadata) { + ItemStack itemStack = entityMetadata.getValue(); + ItemMapping mapping = session.getItemMappings().getMapping(itemStack); + if (mapping.getJavaIdentifier().endsWith("potion") && itemStack.getNbt() != null) { + Tag potionTag = itemStack.getNbt().get("Potion"); + if (potionTag instanceof StringTag) { + Potion potion = Potion.getByJavaIdentifier(((StringTag) potionTag).getValue()); + if (potion != null) { + dirtyMetadata.put(EntityData.POTION_AUX_VALUE, potion.getBedrockId()); + setFlag(EntityFlag.ENCHANTED, !NON_ENCHANTED_POTIONS.contains(potion)); + } else { + dirtyMetadata.put(EntityData.POTION_AUX_VALUE, 0); + GeyserConnector.getInstance().getLogger().debug("Unknown java potion: " + potionTag.getValue()); } - - boolean isLingering = mapping.getJavaIdentifier().equals("minecraft:lingering_potion"); - metadata.getFlags().setFlag(EntityFlag.LINGERING, isLingering); } - } - super.updateBedrockMetadata(entityMetadata, session); + boolean isLingering = mapping.getJavaIdentifier().equals("minecraft:lingering_potion"); + setFlag(EntityFlag.LINGERING, isLingering); + } } } diff --git a/connector/src/main/java/org/geysermc/connector/entity/Tickable.java b/connector/src/main/java/org/geysermc/connector/entity/Tickable.java index a7d571ccb..f5f0188fc 100644 --- a/connector/src/main/java/org/geysermc/connector/entity/Tickable.java +++ b/connector/src/main/java/org/geysermc/connector/entity/Tickable.java @@ -25,11 +25,9 @@ package org.geysermc.connector.entity; -import org.geysermc.connector.network.session.GeyserSession; - /** * Implemented onto anything that should have code ran every Minecraft tick - 50 milliseconds. */ public interface Tickable { - void tick(GeyserSession session); + void tick(); } diff --git a/connector/src/main/java/org/geysermc/connector/entity/TippedArrowEntity.java b/connector/src/main/java/org/geysermc/connector/entity/TippedArrowEntity.java index f7b63435f..d05cb5d2a 100644 --- a/connector/src/main/java/org/geysermc/connector/entity/TippedArrowEntity.java +++ b/connector/src/main/java/org/geysermc/connector/entity/TippedArrowEntity.java @@ -26,38 +26,35 @@ package org.geysermc.connector.entity; import com.github.steveice10.mc.protocol.data.game.entity.metadata.EntityMetadata; +import com.github.steveice10.mc.protocol.data.game.entity.metadata.type.IntEntityMetadata; import com.nukkitx.math.vector.Vector3f; import com.nukkitx.protocol.bedrock.data.entity.EntityData; -import org.geysermc.connector.entity.type.EntityType; import org.geysermc.connector.network.session.GeyserSession; import org.geysermc.connector.network.translators.item.TippedArrowPotion; +import java.util.UUID; + /** * Internally this is known as TippedArrowEntity but is used with tipped arrows and normal arrows */ public class TippedArrowEntity extends AbstractArrowEntity { - public TippedArrowEntity(long entityId, long geyserId, EntityType entityType, Vector3f position, Vector3f motion, Vector3f rotation) { - super(entityId, geyserId, entityType, position, motion, rotation); + public TippedArrowEntity(GeyserSession session, long 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 - public void updateBedrockMetadata(EntityMetadata entityMetadata, GeyserSession session) { - // Arrow potion effect color - if (entityMetadata.getId() == 10) { - int potionColor = (int) entityMetadata.getValue(); - // -1 means no color - if (potionColor == -1) { - metadata.put(EntityData.CUSTOM_DISPLAY, 0); + public void setPotionEffectColor(EntityMetadata entityMetadata) { + int potionColor = ((IntEntityMetadata) entityMetadata).getPrimitiveValue(); + // -1 means no color + if (potionColor == -1) { + dirtyMetadata.put(EntityData.CUSTOM_DISPLAY, 0); + } else { + TippedArrowPotion potion = TippedArrowPotion.getByJavaColor(potionColor); + if (potion != null && potion.getJavaColor() != -1) { + dirtyMetadata.put(EntityData.CUSTOM_DISPLAY, (byte) potion.getBedrockId()); } else { - TippedArrowPotion potion = TippedArrowPotion.getByJavaColor(potionColor); - if (potion != null && potion.getJavaColor() != -1) { - metadata.put(EntityData.CUSTOM_DISPLAY, (byte) potion.getBedrockId()); - } else { - metadata.put(EntityData.CUSTOM_DISPLAY, 0); - } + dirtyMetadata.put(EntityData.CUSTOM_DISPLAY, 0); } } - super.updateBedrockMetadata(entityMetadata, session); } } diff --git a/connector/src/main/java/org/geysermc/connector/entity/TridentEntity.java b/connector/src/main/java/org/geysermc/connector/entity/TridentEntity.java index 19ec27692..5c53ce357 100644 --- a/connector/src/main/java/org/geysermc/connector/entity/TridentEntity.java +++ b/connector/src/main/java/org/geysermc/connector/entity/TridentEntity.java @@ -25,24 +25,14 @@ package org.geysermc.connector.entity; -import com.github.steveice10.mc.protocol.data.game.entity.metadata.EntityMetadata; import com.nukkitx.math.vector.Vector3f; -import com.nukkitx.protocol.bedrock.data.entity.EntityFlag; -import org.geysermc.connector.entity.type.EntityType; import org.geysermc.connector.network.session.GeyserSession; +import java.util.UUID; + public class TridentEntity extends AbstractArrowEntity { - public TridentEntity(long entityId, long geyserId, EntityType entityType, Vector3f position, Vector3f motion, Vector3f rotation) { - super(entityId, geyserId, entityType, position, motion, rotation); - } - - @Override - public void updateBedrockMetadata(EntityMetadata entityMetadata, GeyserSession session) { - if (entityMetadata.getId() == 11) { - metadata.getFlags().setFlag(EntityFlag.ENCHANTED, (boolean) entityMetadata.getValue()); - } - - super.updateBedrockMetadata(entityMetadata, session); + public TridentEntity(GeyserSession session, long 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); } } diff --git a/connector/src/main/java/org/geysermc/connector/entity/WitherSkullEntity.java b/connector/src/main/java/org/geysermc/connector/entity/WitherSkullEntity.java index 6209538dc..936a6c091 100644 --- a/connector/src/main/java/org/geysermc/connector/entity/WitherSkullEntity.java +++ b/connector/src/main/java/org/geysermc/connector/entity/WitherSkullEntity.java @@ -26,35 +26,34 @@ package org.geysermc.connector.entity; import com.github.steveice10.mc.protocol.data.game.entity.metadata.EntityMetadata; +import com.github.steveice10.mc.protocol.data.game.entity.metadata.type.BooleanEntityMetadata; import com.nukkitx.math.vector.Vector3f; -import org.geysermc.connector.entity.type.EntityType; import org.geysermc.connector.network.session.GeyserSession; +import java.util.UUID; + public class WitherSkullEntity extends ItemedFireballEntity { private boolean isCharged; - public WitherSkullEntity(long entityId, long geyserId, EntityType entityType, Vector3f position, Vector3f motion, Vector3f rotation) { - super(entityId, geyserId, entityType, position, motion, rotation); + public WitherSkullEntity(GeyserSession session, long 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); this.futureTicks = 1; } - @Override - protected float getDrag(GeyserSession session) { - return isCharged ? 0.73f : super.getDrag(session); + public void setDangerous(EntityMetadata entityMetadata) { + boolean newDangerous = ((BooleanEntityMetadata) entityMetadata).getPrimitiveValue(); + if (newDangerous != isCharged) { + isCharged = newDangerous; + // Is an entirely new entity in Bedrock but just a metadata type in Java + definition = isCharged ? EntityDefinitions.WITHER_SKULL_DANGEROUS : EntityDefinitions.WITHER_SKULL; + despawnEntity(); + spawnEntity(); + } } @Override - public void updateBedrockMetadata(EntityMetadata entityMetadata, GeyserSession session) { - if (entityMetadata.getId() == 8) { - boolean newIsCharged = (boolean) entityMetadata.getValue(); - if (newIsCharged != isCharged) { - isCharged = newIsCharged; - entityType = isCharged ? EntityType.WITHER_SKULL_DANGEROUS : EntityType.WITHER_SKULL; - despawnEntity(session); - spawnEntity(session); - } - } - super.updateBedrockMetadata(entityMetadata, session); + protected float getDrag() { + return isCharged ? 0.73f : super.getDrag(); } } diff --git a/connector/src/main/java/org/geysermc/connector/entity/factory/BaseEntityFactory.java b/connector/src/main/java/org/geysermc/connector/entity/factory/BaseEntityFactory.java new file mode 100644 index 000000000..67b0e5609 --- /dev/null +++ b/connector/src/main/java/org/geysermc/connector/entity/factory/BaseEntityFactory.java @@ -0,0 +1,39 @@ +/* + * 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.factory; + +import com.nukkitx.math.vector.Vector3f; +import org.geysermc.connector.entity.Entity; +import org.geysermc.connector.entity.EntityDefinition; +import org.geysermc.connector.network.session.GeyserSession; + +import java.util.UUID; + +@FunctionalInterface +public interface BaseEntityFactory extends EntityFactory { + + T create(GeyserSession session, long javaId, long bedrockId, UUID uuid, EntityDefinition definition, Vector3f position, Vector3f motion, float yaw, float pitch, float headYaw); +} diff --git a/connector/src/main/java/org/geysermc/connector/entity/factory/EntityFactory.java b/connector/src/main/java/org/geysermc/connector/entity/factory/EntityFactory.java new file mode 100644 index 000000000..542b9a018 --- /dev/null +++ b/connector/src/main/java/org/geysermc/connector/entity/factory/EntityFactory.java @@ -0,0 +1,31 @@ +/* + * 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.factory; + +import org.geysermc.connector.entity.Entity; + +public interface EntityFactory { +} diff --git a/connector/src/main/java/org/geysermc/connector/entity/factory/ExperienceOrbEntityFactory.java b/connector/src/main/java/org/geysermc/connector/entity/factory/ExperienceOrbEntityFactory.java new file mode 100644 index 000000000..41d8db1c8 --- /dev/null +++ b/connector/src/main/java/org/geysermc/connector/entity/factory/ExperienceOrbEntityFactory.java @@ -0,0 +1,36 @@ +/* + * 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.factory; + +import com.nukkitx.math.vector.Vector3f; +import org.geysermc.connector.entity.ExpOrbEntity; +import org.geysermc.connector.network.session.GeyserSession; + +@FunctionalInterface +public interface ExperienceOrbEntityFactory extends EntityFactory { + + ExpOrbEntity create(GeyserSession session, int amount, long entityId, long geyserId, Vector3f position); +} diff --git a/connector/src/main/java/org/geysermc/connector/entity/factory/PaintingEntityFactory.java b/connector/src/main/java/org/geysermc/connector/entity/factory/PaintingEntityFactory.java new file mode 100644 index 000000000..95bac4c44 --- /dev/null +++ b/connector/src/main/java/org/geysermc/connector/entity/factory/PaintingEntityFactory.java @@ -0,0 +1,38 @@ +/* + * 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.factory; + +import com.nukkitx.math.vector.Vector3f; +import org.geysermc.connector.entity.PaintingEntity; +import org.geysermc.connector.network.session.GeyserSession; +import org.geysermc.connector.utils.PaintingType; + +import java.util.UUID; + +public interface PaintingEntityFactory extends EntityFactory { + + PaintingEntity create(GeyserSession session, long entityId, long geyserId, UUID uuid, Vector3f position, PaintingType paintingName, int direction); +} diff --git a/connector/src/main/java/org/geysermc/connector/entity/living/AbstractFishEntity.java b/connector/src/main/java/org/geysermc/connector/entity/living/AbstractFishEntity.java index 3b80c05fc..43bbf4544 100644 --- a/connector/src/main/java/org/geysermc/connector/entity/living/AbstractFishEntity.java +++ b/connector/src/main/java/org/geysermc/connector/entity/living/AbstractFishEntity.java @@ -27,16 +27,19 @@ package org.geysermc.connector.entity.living; import com.nukkitx.math.vector.Vector3f; import com.nukkitx.protocol.bedrock.data.entity.EntityFlag; -import org.geysermc.connector.entity.type.EntityType; +import org.geysermc.connector.entity.EntityDefinition; +import org.geysermc.connector.network.session.GeyserSession; + +import java.util.UUID; public class AbstractFishEntity extends WaterEntity { - public AbstractFishEntity(long entityId, long geyserId, EntityType entityType, Vector3f position, Vector3f motion, Vector3f rotation) { - super(entityId, geyserId, entityType, position, motion, rotation); + public AbstractFishEntity(GeyserSession session, long 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); - metadata.getFlags().setFlag(EntityFlag.CAN_SWIM, true); - metadata.getFlags().setFlag(EntityFlag.BREATHING, true); - metadata.getFlags().setFlag(EntityFlag.CAN_CLIMB, false); - metadata.getFlags().setFlag(EntityFlag.HAS_GRAVITY, false); + setFlag(EntityFlag.CAN_SWIM, true); + setFlag(EntityFlag.BREATHING, true); + setFlag(EntityFlag.CAN_CLIMB, false); + setFlag(EntityFlag.HAS_GRAVITY, false); } } diff --git a/connector/src/main/java/org/geysermc/connector/entity/living/AgeableEntity.java b/connector/src/main/java/org/geysermc/connector/entity/living/AgeableEntity.java index 8f8166d07..b864b8e7b 100644 --- a/connector/src/main/java/org/geysermc/connector/entity/living/AgeableEntity.java +++ b/connector/src/main/java/org/geysermc/connector/entity/living/AgeableEntity.java @@ -26,29 +26,46 @@ package org.geysermc.connector.entity.living; import com.github.steveice10.mc.protocol.data.game.entity.metadata.EntityMetadata; +import com.github.steveice10.mc.protocol.data.game.entity.metadata.type.BooleanEntityMetadata; import com.nukkitx.math.vector.Vector3f; import com.nukkitx.protocol.bedrock.data.entity.EntityData; import com.nukkitx.protocol.bedrock.data.entity.EntityFlag; -import org.geysermc.connector.entity.type.EntityType; +import org.geysermc.connector.entity.EntityDefinition; import org.geysermc.connector.network.session.GeyserSession; +import java.util.UUID; + public class AgeableEntity extends CreatureEntity { - public AgeableEntity(long entityId, long geyserId, EntityType entityType, Vector3f position, Vector3f motion, Vector3f rotation) { - super(entityId, geyserId, entityType, position, motion, rotation); + public AgeableEntity(GeyserSession session, long 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 - public void updateBedrockMetadata(EntityMetadata entityMetadata, GeyserSession session) { - if (entityMetadata.getId() == 16) { - boolean isBaby = (boolean) entityMetadata.getValue(); - metadata.put(EntityData.SCALE, isBaby ? .55f : 1f); - metadata.getFlags().setFlag(EntityFlag.BABY, isBaby); + public void setBaby(EntityMetadata entityMetadata) { + boolean isBaby = ((BooleanEntityMetadata) entityMetadata).getPrimitiveValue(); + dirtyMetadata.put(EntityData.SCALE, isBaby ? getBabySize() : getAdultSize()); + setFlag(EntityFlag.BABY, isBaby); - metadata.put(EntityData.BOUNDING_BOX_HEIGHT, entityType.getHeight() * (isBaby ? 0.55f : 1f)); - metadata.put(EntityData.BOUNDING_BOX_WIDTH, entityType.getWidth() * (isBaby ? 0.55f : 1f)); - } + // TODO save this? + dirtyMetadata.put(EntityData.BOUNDING_BOX_HEIGHT, definition.height() * (isBaby ? getBabySize() : getAdultSize())); + dirtyMetadata.put(EntityData.BOUNDING_BOX_WIDTH, definition.width() * (isBaby ? getBabySize() : getAdultSize())); + } - super.updateBedrockMetadata(entityMetadata, session); + /** + * The scale that should be used when this entity is not a baby. + */ + protected float getAdultSize() { + return 1f; + } + + /** + * The scale that should be used when this entity is a baby. + */ + protected float getBabySize() { + return 0.55f; + } + + public boolean isBaby() { + return getFlag(EntityFlag.BABY); } } diff --git a/connector/src/main/java/org/geysermc/connector/entity/living/AmbientEntity.java b/connector/src/main/java/org/geysermc/connector/entity/living/AmbientEntity.java index cc5fd2111..38e3483a6 100644 --- a/connector/src/main/java/org/geysermc/connector/entity/living/AmbientEntity.java +++ b/connector/src/main/java/org/geysermc/connector/entity/living/AmbientEntity.java @@ -26,11 +26,14 @@ package org.geysermc.connector.entity.living; import com.nukkitx.math.vector.Vector3f; -import org.geysermc.connector.entity.type.EntityType; +import org.geysermc.connector.entity.EntityDefinition; +import org.geysermc.connector.network.session.GeyserSession; -public class AmbientEntity extends InsentientEntity { +import java.util.UUID; - public AmbientEntity(long entityId, long geyserId, EntityType entityType, Vector3f position, Vector3f motion, Vector3f rotation) { - super(entityId, geyserId, entityType, position, motion, rotation); +public class AmbientEntity extends MobEntity { + + public AmbientEntity(GeyserSession session, long 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); } } diff --git a/connector/src/main/java/org/geysermc/connector/entity/living/ArmorStandEntity.java b/connector/src/main/java/org/geysermc/connector/entity/living/ArmorStandEntity.java index 6a87097a1..904de233f 100644 --- a/connector/src/main/java/org/geysermc/connector/entity/living/ArmorStandEntity.java +++ b/connector/src/main/java/org/geysermc/connector/entity/living/ArmorStandEntity.java @@ -26,18 +26,22 @@ package org.geysermc.connector.entity.living; import com.github.steveice10.mc.protocol.data.game.entity.metadata.EntityMetadata; -import com.github.steveice10.mc.protocol.data.game.entity.metadata.MetadataType; import com.github.steveice10.mc.protocol.data.game.entity.metadata.Rotation; +import com.github.steveice10.mc.protocol.data.game.entity.metadata.type.ByteEntityMetadata; import com.nukkitx.math.vector.Vector3f; import com.nukkitx.protocol.bedrock.data.entity.EntityData; import com.nukkitx.protocol.bedrock.data.entity.EntityFlag; import com.nukkitx.protocol.bedrock.data.inventory.ItemData; import com.nukkitx.protocol.bedrock.packet.MoveEntityAbsolutePacket; import lombok.Getter; +import net.kyori.adventure.text.Component; +import org.geysermc.connector.entity.EntityDefinition; +import org.geysermc.connector.entity.EntityDefinitions; import org.geysermc.connector.entity.LivingEntity; -import org.geysermc.connector.entity.type.EntityType; import org.geysermc.connector.network.session.GeyserSession; +import java.util.UUID; + public class ArmorStandEntity extends LivingEntity { // These are used to store the state of the armour stand for use when handling invisibility @@ -72,162 +76,150 @@ public class ArmorStandEntity extends LivingEntity { * Whether we should update the position of this armor stand after metadata updates. */ private boolean positionUpdateRequired = false; - private GeyserSession session; - public ArmorStandEntity(long entityId, long geyserId, EntityType entityType, Vector3f position, Vector3f motion, Vector3f rotation) { - super(entityId, geyserId, entityType, position, motion, rotation); + public ArmorStandEntity(GeyserSession session, long 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 - public void spawnEntity(GeyserSession session) { - this.session = session; - this.rotation = Vector3f.from(rotation.getX(), rotation.getX(), rotation.getX()); - super.spawnEntity(session); + public void spawnEntity() { + this.pitch = yaw; + this.headYaw = yaw; + super.spawnEntity(); } @Override - public boolean despawnEntity(GeyserSession session) { + public boolean despawnEntity() { if (secondEntity != null) { - secondEntity.despawnEntity(session); + secondEntity.despawnEntity(); } - return super.despawnEntity(session); + return super.despawnEntity(); } @Override - public void moveRelative(GeyserSession session, double relX, double relY, double relZ, Vector3f rotation, boolean isOnGround) { + public void moveRelative(double relX, double relY, double relZ, float yaw, float pitch, float headYaw, boolean isOnGround) { if (secondEntity != null) { - secondEntity.moveRelative(session, relX, relY, relZ, rotation, isOnGround); + secondEntity.moveRelative(relX, relY, relZ, yaw, pitch, headYaw, isOnGround); } - super.moveRelative(session, relX, relY, relZ, rotation, isOnGround); + super.moveRelative(relX, relY, relZ, yaw, pitch, headYaw, isOnGround); } @Override - public void moveAbsolute(GeyserSession session, Vector3f position, Vector3f rotation, boolean isOnGround, boolean teleported) { + public void moveAbsolute(Vector3f position, float yaw, float pitch, float headYaw, boolean isOnGround, boolean teleported) { if (secondEntity != null) { - secondEntity.moveAbsolute(session, applyOffsetToPosition(position), rotation, isOnGround, teleported); + secondEntity.moveAbsolute(applyOffsetToPosition(position), yaw, pitch, headYaw, isOnGround, teleported); } else if (positionRequiresOffset) { // Fake the height to be above where it is so the nametag appears in the right location for invisible non-marker armour stands position = applyOffsetToPosition(position); } - super.moveAbsolute(session, position, Vector3f.from(rotation.getX(), rotation.getX(), rotation.getX()), isOnGround, teleported); + super.moveAbsolute(position, yaw, yaw, yaw, isOnGround, teleported); } @Override - public void updateBedrockMetadata(EntityMetadata entityMetadata, GeyserSession session) { - super.updateBedrockMetadata(entityMetadata, session); - if (entityMetadata.getId() == 2) { + public void setDisplayName(EntityMetadata entityMetadata) { + super.setDisplayName(entityMetadata); + updateSecondEntityStatus(false); + } + + public void setArmorStandFlags(EntityMetadata entityMetadata) { + byte xd = ((ByteEntityMetadata) entityMetadata).getPrimitiveValue(); + + // isSmall + boolean newIsSmall = (xd & 0x01) == 0x01; + if (newIsSmall != isSmall) { + if (positionRequiresOffset) { + // Fix new inconsistency with offset + this.position = fixOffsetForSize(position, newIsSmall); + positionUpdateRequired = true; + } + + isSmall = newIsSmall; + if (!isMarker) { + toggleSmallStatus(); + } + } + + // setMarker + boolean oldIsMarker = isMarker; + isMarker = (xd & 0x10) == 0x10; + if (oldIsMarker != isMarker) { + if (isMarker) { + dirtyMetadata.put(EntityData.BOUNDING_BOX_WIDTH, 0.0f); + dirtyMetadata.put(EntityData.BOUNDING_BOX_HEIGHT, 0.0f); + dirtyMetadata.put(EntityData.SCALE, 0f); + } else { + toggleSmallStatus(); + } + updateSecondEntityStatus(false); - } else if (entityMetadata.getId() == 15 && entityMetadata.getType() == MetadataType.BYTE) { - byte xd = (byte) entityMetadata.getValue(); - - // isSmall - boolean newIsSmall = (xd & 0x01) == 0x01; - if (newIsSmall != isSmall) { - if (positionRequiresOffset) { - // Fix new inconsistency with offset - this.position = fixOffsetForSize(position, newIsSmall); - positionUpdateRequired = true; - } - - isSmall = newIsSmall; - if (!isMarker) { - toggleSmallStatus(); - } - } - - // setMarker - boolean oldIsMarker = isMarker; - isMarker = (xd & 0x10) == 0x10; - if (oldIsMarker != isMarker) { - if (isMarker) { - metadata.put(EntityData.BOUNDING_BOX_WIDTH, 0.0f); - metadata.put(EntityData.BOUNDING_BOX_HEIGHT, 0.0f); - metadata.put(EntityData.SCALE, 0f); - } else { - toggleSmallStatus(); - } - - updateSecondEntityStatus(false); - } - - // The following values don't do anything on normal Bedrock. - // But if given a resource pack, then we can use these values to control armor stand visual properties - metadata.getFlags().setFlag(EntityFlag.ANGRY, (xd & 0x04) != 0x04); // Has arms - metadata.getFlags().setFlag(EntityFlag.ADMIRING, (xd & 0x08) == 0x08); // Has no baseplate - } else { - EntityData dataLeech = null; - EntityFlag negativeXToggle = null; - EntityFlag negativeYToggle = null; - EntityFlag negativeZToggle = null; - switch (entityMetadata.getId()) { - case 16 -> { // Head - dataLeech = EntityData.MARK_VARIANT; - negativeXToggle = EntityFlag.INTERESTED; - negativeYToggle = EntityFlag.CHARGED; - negativeZToggle = EntityFlag.POWERED; - } - case 17 -> { // Body - dataLeech = EntityData.VARIANT; - negativeXToggle = EntityFlag.IN_LOVE; - negativeYToggle = EntityFlag.CELEBRATING; - negativeZToggle = EntityFlag.CELEBRATING_SPECIAL; - } - case 18 -> { // Left arm - dataLeech = EntityData.TRADE_TIER; - negativeXToggle = EntityFlag.CHARGING; - negativeYToggle = EntityFlag.CRITICAL; - negativeZToggle = EntityFlag.DANCING; - } - case 19 -> { // Right arm - dataLeech = EntityData.MAX_TRADE_TIER; - negativeXToggle = EntityFlag.ELDER; - negativeYToggle = EntityFlag.EMOTING; - negativeZToggle = EntityFlag.IDLING; - } - case 20 -> { // Left leg - dataLeech = EntityData.SKIN_ID; - negativeXToggle = EntityFlag.IS_ILLAGER_CAPTAIN; - negativeYToggle = EntityFlag.IS_IN_UI; - negativeZToggle = EntityFlag.LINGERING; - } - case 21 -> { // Right leg - dataLeech = EntityData.HURT_DIRECTION; - negativeXToggle = EntityFlag.IS_PREGNANT; - negativeYToggle = EntityFlag.SHEARED; - negativeZToggle = EntityFlag.STALKING; - } - } - if (dataLeech != null) { - // Indicate that rotation should be checked - metadata.getFlags().setFlag(EntityFlag.BRIBED, true); - - Rotation rotation = (Rotation) entityMetadata.getValue(); - int rotationX = getRotation(rotation.getPitch()); - int rotationY = getRotation(rotation.getYaw()); - int rotationZ = getRotation(rotation.getRoll()); - // The top bit acts like binary and determines if each rotation goes above 100 - // We don't do this for the negative values out of concerns of the number being too big - int topBit = (Math.abs(rotationX) >= 100 ? 4 : 0) + (Math.abs(rotationY) >= 100 ? 2 : 0) + (Math.abs(rotationZ) >= 100 ? 1 : 0); - int value = (topBit * 1000000) + ((Math.abs(rotationX) % 100) * 10000) + ((Math.abs(rotationY) % 100) * 100) + (Math.abs(rotationZ) % 100); - metadata.put(dataLeech, value); - // Set the entity flags if a value is negative - metadata.getFlags().setFlag(negativeXToggle, rotationX < 0); - metadata.getFlags().setFlag(negativeYToggle, rotationY < 0); - metadata.getFlags().setFlag(negativeZToggle, rotationZ < 0); - } - } - if (secondEntity != null) { - secondEntity.updateBedrockMetadata(entityMetadata, session); } + + // The following values don't do anything on normal Bedrock. + // But if given a resource pack, then we can use these values to control armor stand visual properties + setFlag(EntityFlag.ANGRY, (xd & 0x04) != 0x04); // Has arms + setFlag(EntityFlag.ADMIRING, (xd & 0x08) == 0x08); // Has no baseplate + } + + public void setHeadRotation(EntityMetadata entityMetadata) { + onRotationUpdate(EntityData.MARK_VARIANT, EntityFlag.INTERESTED, EntityFlag.CHARGED, EntityFlag.POWERED, entityMetadata.getValue()); + } + + public void setBodyRotation(EntityMetadata entityMetadata) { + onRotationUpdate(EntityData.VARIANT, EntityFlag.IN_LOVE, EntityFlag.CELEBRATING, EntityFlag.CELEBRATING_SPECIAL, entityMetadata.getValue()); + } + + public void setLeftArmRotation(EntityMetadata entityMetadata) { + onRotationUpdate(EntityData.TRADE_TIER, EntityFlag.CHARGING, EntityFlag.CRITICAL, EntityFlag.DANCING, entityMetadata.getValue()); + } + + public void setRightArmRotation(EntityMetadata entityMetadata) { + onRotationUpdate(EntityData.MAX_TRADE_TIER, EntityFlag.ELDER, EntityFlag.EMOTING, EntityFlag.IDLING, entityMetadata.getValue()); + } + + public void setLeftLegRotation(EntityMetadata entityMetadata) { + onRotationUpdate(EntityData.SKIN_ID, EntityFlag.IS_ILLAGER_CAPTAIN, EntityFlag.IS_IN_UI, EntityFlag.LINGERING, entityMetadata.getValue()); + } + + public void setRightLegRotation(EntityMetadata entityMetadata) { + onRotationUpdate(EntityData.HURT_DIRECTION, EntityFlag.IS_PREGNANT, EntityFlag.SHEARED, EntityFlag.STALKING, entityMetadata.getValue()); + } + + /** + * Updates rotation on the armor stand by hijacking other unused Bedrock entity data/flags. + * Do note: as of recent Bedrock versions there is a custom entity data system that can be replaced with this, + * but at this time there is no need to implement this. + * + * @param dataLeech the entity data to "leech" off of that stores a compressed version of the rotation + * @param negativeXToggle the flag to set true if the X value of rotation is negative + * @param negativeYToggle the flag to set true if the Y value of rotation is negative + * @param negativeZToggle the flag to set true if the Z value of rotation is negative + * @param rotation the Java rotation value + */ + private void onRotationUpdate(EntityData dataLeech, EntityFlag negativeXToggle, EntityFlag negativeYToggle, EntityFlag negativeZToggle, Rotation rotation) { + // Indicate that rotation should be checked + setFlag(EntityFlag.BRIBED, true); + + int rotationX = getRotation(rotation.getPitch()); + int rotationY = getRotation(rotation.getYaw()); + int rotationZ = getRotation(rotation.getRoll()); + // The top bit acts like binary and determines if each rotation goes above 100 + // We don't do this for the negative values out of concerns of the number being too big + int topBit = (Math.abs(rotationX) >= 100 ? 4 : 0) + (Math.abs(rotationY) >= 100 ? 2 : 0) + (Math.abs(rotationZ) >= 100 ? 1 : 0); + int value = (topBit * 1000000) + ((Math.abs(rotationX) % 100) * 10000) + ((Math.abs(rotationY) % 100) * 100) + (Math.abs(rotationZ) % 100); + dirtyMetadata.put(dataLeech, value); + // Set the entity flags if a value is negative + setFlag(negativeXToggle, rotationX < 0); + setFlag(negativeYToggle, rotationY < 0); + setFlag(negativeZToggle, rotationZ < 0); } @Override - public void updateBedrockMetadata(GeyserSession session) { + public void updateBedrockMetadata() { if (secondEntity != null) { - secondEntity.updateBedrockMetadata(session); + secondEntity.updateBedrockMetadata(); } - super.updateBedrockMetadata(session); + super.updateBedrockMetadata(); if (positionUpdateRequired) { positionUpdateRequired = false; updatePosition(); @@ -235,7 +227,7 @@ public class ArmorStandEntity extends LivingEntity { } @Override - protected void setInvisible(GeyserSession session, boolean value) { + protected void setInvisible(boolean value) { // Check if the armour stand is invisible and store accordingly if (primaryEntity) { isInvisible = value; @@ -289,7 +281,7 @@ public class ArmorStandEntity extends LivingEntity { if (!primaryEntity) return; if (!isInvisible || isMarker) { // It is either impossible to show armor, or the armor stand isn't invisible. We good. - metadata.getFlags().setFlag(EntityFlag.INVISIBLE, false); + setFlag(EntityFlag.INVISIBLE, false); updateOffsetRequirement(false); if (positionUpdateRequired) { positionUpdateRequired = false; @@ -297,13 +289,13 @@ public class ArmorStandEntity extends LivingEntity { } if (secondEntity != null) { - secondEntity.despawnEntity(session); + secondEntity.despawnEntity(); secondEntity = null; } return; } //boolean isNametagEmpty = metadata.getString(EntityData.NAMETAG).isEmpty() || metadata.getByte(EntityData.NAMETAG_ALWAYS_SHOW, (byte) -1) == (byte) 0; - may not be necessary? - boolean isNametagEmpty = metadata.getString(EntityData.NAMETAG).isEmpty(); + boolean isNametagEmpty = dirtyMetadata.getString(EntityData.NAMETAG).isEmpty(); // TODO if (!isNametagEmpty && (!helmet.equals(ItemData.AIR) || !chestplate.equals(ItemData.AIR) || !leggings.equals(ItemData.AIR) || !boots.equals(ItemData.AIR) || !hand.equals(ItemData.AIR) || !offHand.equals(ItemData.AIR))) { // If the second entity exists, no need to recreate it. @@ -312,8 +304,8 @@ public class ArmorStandEntity extends LivingEntity { // Create the second entity. It doesn't need to worry about the items, but it does need to worry about // the metadata as it will hold the name tag. - secondEntity = new ArmorStandEntity(0, session.getEntityCache().getNextEntityId().incrementAndGet(), - EntityType.ARMOR_STAND, position, motion, rotation); + secondEntity = new ArmorStandEntity(session, 0, session.getEntityCache().getNextEntityId().incrementAndGet(), null, + EntityDefinitions.ARMOR_STAND, position, motion, yaw, pitch, headYaw); secondEntity.primaryEntity = false; if (!this.positionRequiresOffset) { // Ensure the offset is applied for the 0 scale @@ -321,52 +313,51 @@ public class ArmorStandEntity extends LivingEntity { } // Copy metadata secondEntity.isSmall = isSmall; - secondEntity.getMetadata().putAll(metadata); - // Copy the flags so they aren't the same object in memory - secondEntity.getMetadata().putFlags(metadata.getFlags().copy()); + secondEntity.getDirtyMetadata().putAll(dirtyMetadata); //TODO check + secondEntity.flags.merge(this.flags); // Guarantee this copy is NOT invisible - secondEntity.getMetadata().getFlags().setFlag(EntityFlag.INVISIBLE, false); + secondEntity.setFlag(EntityFlag.INVISIBLE, false); // Scale to 0 to show nametag - secondEntity.getMetadata().put(EntityData.SCALE, 0.0f); + secondEntity.getDirtyMetadata().put(EntityData.SCALE, 0.0f); // No bounding box as we don't want to interact with this entity - secondEntity.getMetadata().put(EntityData.BOUNDING_BOX_WIDTH, 0.0f); - secondEntity.getMetadata().put(EntityData.BOUNDING_BOX_HEIGHT, 0.0f); - secondEntity.spawnEntity(session); + secondEntity.getDirtyMetadata().put(EntityData.BOUNDING_BOX_WIDTH, 0.0f); + secondEntity.getDirtyMetadata().put(EntityData.BOUNDING_BOX_HEIGHT, 0.0f); + secondEntity.spawnEntity(); // Reset scale of the proper armor stand - this.metadata.put(EntityData.SCALE, isSmall ? 0.55f : 1f); + this.dirtyMetadata.put(EntityData.SCALE, isSmall ? 0.55f : 1f); // Set the proper armor stand to invisible to show armor - this.metadata.getFlags().setFlag(EntityFlag.INVISIBLE, true); + setFlag(EntityFlag.INVISIBLE, true); // Update the position of the armor stand updateOffsetRequirement(false); } else if (isNametagEmpty) { // We can just make an invisible entity // Reset scale of the proper armor stand - metadata.put(EntityData.SCALE, isSmall ? 0.55f : 1f); + dirtyMetadata.put(EntityData.SCALE, isSmall ? 0.55f : 1f); // Set the proper armor stand to invisible to show armor - metadata.getFlags().setFlag(EntityFlag.INVISIBLE, true); + setFlag(EntityFlag.INVISIBLE, true); // Update offset updateOffsetRequirement(false); if (secondEntity != null) { - secondEntity.despawnEntity(session); + secondEntity.despawnEntity(); secondEntity = null; } } else { // Nametag is not empty and there is no armor // We don't need to make a new entity - metadata.getFlags().setFlag(EntityFlag.INVISIBLE, false); - metadata.put(EntityData.SCALE, 0.0f); + setFlag(EntityFlag.INVISIBLE, false); + dirtyMetadata.put(EntityData.SCALE, 0.0f); // As the above is applied, we need an offset updateOffsetRequirement(true); if (secondEntity != null) { - secondEntity.despawnEntity(session); + secondEntity.despawnEntity(); secondEntity = null; } } if (sendMetadata) { - this.updateBedrockMetadata(session); + this.updateBedrockMetadata(); } } @@ -385,16 +376,16 @@ public class ArmorStandEntity extends LivingEntity { * If this armor stand is not a marker, set its bounding box size and scale. */ private void toggleSmallStatus() { - metadata.put(EntityData.BOUNDING_BOX_WIDTH, isSmall ? 0.25f : entityType.getWidth()); - metadata.put(EntityData.BOUNDING_BOX_HEIGHT, isSmall ? 0.9875f : entityType.getHeight()); - metadata.put(EntityData.SCALE, isSmall ? 0.55f : 1f); + dirtyMetadata.put(EntityData.BOUNDING_BOX_WIDTH, isSmall ? 0.25f : definition.width()); + dirtyMetadata.put(EntityData.BOUNDING_BOX_HEIGHT, isSmall ? 0.9875f : definition.height()); + dirtyMetadata.put(EntityData.SCALE, isSmall ? 0.55f : 1f); } /** * @return the selected position with the position offset applied. */ private Vector3f applyOffsetToPosition(Vector3f position) { - return position.add(0d, entityType.getHeight() * (isSmall ? 0.55d : 1d), 0d); + return position.add(0d, definition.height() * (isSmall ? 0.55d : 1d), 0d); } /** @@ -402,14 +393,14 @@ public class ArmorStandEntity extends LivingEntity { */ private Vector3f fixOffsetForSize(Vector3f position, boolean isNowSmall) { position = removeOffsetFromPosition(position); - return position.add(0d, entityType.getHeight() * (isNowSmall ? 0.55d : 1d), 0d); + return position.add(0d, definition.height() * (isNowSmall ? 0.55d : 1d), 0d); } /** * @return the selected position with the position offset removed. */ private Vector3f removeOffsetFromPosition(Vector3f position) { - return position.sub(0d, entityType.getHeight() * (isSmall ? 0.55d : 1d), 0d); + return position.sub(0d, definition.height() * (isSmall ? 0.55d : 1d), 0d); } /** @@ -434,7 +425,7 @@ public class ArmorStandEntity extends LivingEntity { MoveEntityAbsolutePacket moveEntityPacket = new MoveEntityAbsolutePacket(); moveEntityPacket.setRuntimeEntityId(geyserId); moveEntityPacket.setPosition(position); - moveEntityPacket.setRotation(Vector3f.from(rotation.getX(), rotation.getX(), rotation.getX())); + moveEntityPacket.setRotation(Vector3f.from(yaw, yaw, yaw)); moveEntityPacket.setOnGround(onGround); moveEntityPacket.setTeleported(false); session.sendUpstreamPacket(moveEntityPacket); diff --git a/connector/src/main/java/org/geysermc/connector/entity/living/BatEntity.java b/connector/src/main/java/org/geysermc/connector/entity/living/BatEntity.java index 110c02e06..4f5bd1ead 100644 --- a/connector/src/main/java/org/geysermc/connector/entity/living/BatEntity.java +++ b/connector/src/main/java/org/geysermc/connector/entity/living/BatEntity.java @@ -26,23 +26,22 @@ package org.geysermc.connector.entity.living; import com.github.steveice10.mc.protocol.data.game.entity.metadata.EntityMetadata; +import com.github.steveice10.mc.protocol.data.game.entity.metadata.type.ByteEntityMetadata; import com.nukkitx.math.vector.Vector3f; import com.nukkitx.protocol.bedrock.data.entity.EntityFlag; -import org.geysermc.connector.entity.type.EntityType; +import org.geysermc.connector.entity.EntityDefinition; import org.geysermc.connector.network.session.GeyserSession; +import java.util.UUID; + public class BatEntity extends AmbientEntity { - public BatEntity(long entityId, long geyserId, EntityType entityType, Vector3f position, Vector3f motion, Vector3f rotation) { - super(entityId, geyserId, entityType, position, motion, rotation); + public BatEntity(GeyserSession session, long 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 - public void updateBedrockMetadata(EntityMetadata entityMetadata, GeyserSession session) { - if (entityMetadata.getId() == 16) { - byte xd = (byte) entityMetadata.getValue(); - metadata.getFlags().setFlag(EntityFlag.RESTING, (xd & 0x01) == 0x01); - } - super.updateBedrockMetadata(entityMetadata, session); + public void setBatFlags(EntityMetadata entityMetadata) { + byte xd = ((ByteEntityMetadata) entityMetadata).getPrimitiveValue(); + setFlag(EntityFlag.RESTING, (xd & 0x01) == 0x01); } } diff --git a/connector/src/main/java/org/geysermc/connector/entity/living/CreatureEntity.java b/connector/src/main/java/org/geysermc/connector/entity/living/CreatureEntity.java index e2cc5a6f4..e0407e0fc 100644 --- a/connector/src/main/java/org/geysermc/connector/entity/living/CreatureEntity.java +++ b/connector/src/main/java/org/geysermc/connector/entity/living/CreatureEntity.java @@ -26,11 +26,14 @@ package org.geysermc.connector.entity.living; import com.nukkitx.math.vector.Vector3f; -import org.geysermc.connector.entity.type.EntityType; +import org.geysermc.connector.entity.EntityDefinition; +import org.geysermc.connector.network.session.GeyserSession; -public class CreatureEntity extends InsentientEntity { +import java.util.UUID; - public CreatureEntity(long entityId, long geyserId, EntityType entityType, Vector3f position, Vector3f motion, Vector3f rotation) { - super(entityId, geyserId, entityType, position, motion, rotation); +public class CreatureEntity extends MobEntity { + + public CreatureEntity(GeyserSession session, long 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); } } diff --git a/connector/src/main/java/org/geysermc/connector/entity/living/FlyingEntity.java b/connector/src/main/java/org/geysermc/connector/entity/living/FlyingEntity.java index 6bcfe79f1..1cf362985 100644 --- a/connector/src/main/java/org/geysermc/connector/entity/living/FlyingEntity.java +++ b/connector/src/main/java/org/geysermc/connector/entity/living/FlyingEntity.java @@ -26,11 +26,14 @@ package org.geysermc.connector.entity.living; import com.nukkitx.math.vector.Vector3f; -import org.geysermc.connector.entity.type.EntityType; +import org.geysermc.connector.entity.EntityDefinition; +import org.geysermc.connector.network.session.GeyserSession; -public class FlyingEntity extends InsentientEntity { +import java.util.UUID; - public FlyingEntity(long entityId, long geyserId, EntityType entityType, Vector3f position, Vector3f motion, Vector3f rotation) { - super(entityId, geyserId, entityType, position, motion, rotation); +public class FlyingEntity extends MobEntity { + + public FlyingEntity(GeyserSession session, long 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); } } diff --git a/connector/src/main/java/org/geysermc/connector/entity/living/GlowSquidEntity.java b/connector/src/main/java/org/geysermc/connector/entity/living/GlowSquidEntity.java index b68c5d224..f99c9ee4d 100644 --- a/connector/src/main/java/org/geysermc/connector/entity/living/GlowSquidEntity.java +++ b/connector/src/main/java/org/geysermc/connector/entity/living/GlowSquidEntity.java @@ -25,19 +25,14 @@ package org.geysermc.connector.entity.living; -import com.github.steveice10.mc.protocol.data.game.entity.metadata.EntityMetadata; import com.nukkitx.math.vector.Vector3f; -import org.geysermc.connector.entity.type.EntityType; +import org.geysermc.connector.entity.EntityDefinition; import org.geysermc.connector.network.session.GeyserSession; -public class GlowSquidEntity extends SquidEntity { - public GlowSquidEntity(long entityId, long geyserId, EntityType entityType, Vector3f position, Vector3f motion, Vector3f rotation) { - super(entityId, geyserId, entityType, position, motion, rotation); - } +import java.util.UUID; - @Override - public void updateBedrockMetadata(EntityMetadata entityMetadata, GeyserSession session) { - super.updateBedrockMetadata(entityMetadata, session); - // TODO "dark ticks remaining" ??? does this have a Bedrock equivalent? +public class GlowSquidEntity extends SquidEntity { + public GlowSquidEntity(GeyserSession session, long 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); } } diff --git a/connector/src/main/java/org/geysermc/connector/entity/living/GolemEntity.java b/connector/src/main/java/org/geysermc/connector/entity/living/GolemEntity.java index 2f202ee9f..da88f5acc 100644 --- a/connector/src/main/java/org/geysermc/connector/entity/living/GolemEntity.java +++ b/connector/src/main/java/org/geysermc/connector/entity/living/GolemEntity.java @@ -26,11 +26,14 @@ package org.geysermc.connector.entity.living; import com.nukkitx.math.vector.Vector3f; -import org.geysermc.connector.entity.type.EntityType; +import org.geysermc.connector.entity.EntityDefinition; +import org.geysermc.connector.network.session.GeyserSession; + +import java.util.UUID; public class GolemEntity extends CreatureEntity { - public GolemEntity(long entityId, long geyserId, EntityType entityType, Vector3f position, Vector3f motion, Vector3f rotation) { - super(entityId, geyserId, entityType, position, motion, rotation); + public GolemEntity(GeyserSession session, long 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); } } diff --git a/connector/src/main/java/org/geysermc/connector/entity/living/IronGolemEntity.java b/connector/src/main/java/org/geysermc/connector/entity/living/IronGolemEntity.java index a7b7a55c6..21e495239 100644 --- a/connector/src/main/java/org/geysermc/connector/entity/living/IronGolemEntity.java +++ b/connector/src/main/java/org/geysermc/connector/entity/living/IronGolemEntity.java @@ -28,15 +28,18 @@ package org.geysermc.connector.entity.living; import com.nukkitx.math.vector.Vector3f; import com.nukkitx.protocol.bedrock.data.entity.EntityData; import com.nukkitx.protocol.bedrock.data.entity.EntityFlag; -import org.geysermc.connector.entity.type.EntityType; +import org.geysermc.connector.entity.EntityDefinition; +import org.geysermc.connector.network.session.GeyserSession; + +import java.util.UUID; public class IronGolemEntity extends GolemEntity { - public IronGolemEntity(long entityId, long geyserId, EntityType entityType, Vector3f position, Vector3f motion, Vector3f rotation) { - super(entityId, geyserId, entityType, position, motion, rotation); + public IronGolemEntity(GeyserSession session, long 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); // Indicate that we should show cracks through a resource pack - metadata.getFlags().setFlag(EntityFlag.BRIBED, true); + setFlag(EntityFlag.BRIBED, true); // Required, or else the overlay is black - metadata.put(EntityData.COLOR_2, (byte) 0); + dirtyMetadata.put(EntityData.COLOR_2, (byte) 0); } } diff --git a/connector/src/main/java/org/geysermc/connector/entity/living/MagmaCubeEntity.java b/connector/src/main/java/org/geysermc/connector/entity/living/MagmaCubeEntity.java index fb2726d1d..bdbac85a7 100644 --- a/connector/src/main/java/org/geysermc/connector/entity/living/MagmaCubeEntity.java +++ b/connector/src/main/java/org/geysermc/connector/entity/living/MagmaCubeEntity.java @@ -27,32 +27,34 @@ package org.geysermc.connector.entity.living; import com.nukkitx.math.vector.Vector3f; import com.nukkitx.protocol.bedrock.data.entity.EntityData; -import org.geysermc.connector.entity.type.EntityType; +import org.geysermc.connector.entity.EntityDefinition; import org.geysermc.connector.network.session.GeyserSession; +import java.util.UUID; + public class MagmaCubeEntity extends SlimeEntity { - public MagmaCubeEntity(long entityId, long geyserId, EntityType entityType, Vector3f position, Vector3f motion, Vector3f rotation) { - super(entityId, geyserId, entityType, position, motion, rotation); + public MagmaCubeEntity(GeyserSession session, long 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 - public void moveRelative(GeyserSession session, double relX, double relY, double relZ, Vector3f rotation, boolean isOnGround) { - updateJump(session, isOnGround); - super.moveRelative(session, relX, relY, relZ, rotation, isOnGround); + public void moveRelative(double relX, double relY, double relZ, float yaw, float pitch, float headYaw, boolean isOnGround) { + updateJump(isOnGround); + super.moveRelative(relX, relY, relZ, yaw, pitch, headYaw, isOnGround); } @Override - public void moveAbsolute(GeyserSession session, Vector3f position, Vector3f rotation, boolean isOnGround, boolean teleported) { - updateJump(session, isOnGround); - super.moveAbsolute(session, position, rotation, isOnGround, teleported); + public void moveAbsolute(Vector3f position, float yaw, float pitch, float headYaw, boolean isOnGround, boolean teleported) { + updateJump(isOnGround); + super.moveAbsolute(position, yaw, pitch, headYaw, isOnGround, teleported); } - public void updateJump(GeyserSession session, boolean newOnGround) { + public void updateJump(boolean newOnGround) { if (newOnGround != onGround) { // Add the jumping effect to the magma cube - metadata.put(EntityData.CLIENT_EVENT, (byte) (newOnGround ? 1 : 2)); - updateBedrockMetadata(session); + dirtyMetadata.put(EntityData.CLIENT_EVENT, (byte) (newOnGround ? 1 : 2)); + updateBedrockMetadata(); } } } diff --git a/connector/src/main/java/org/geysermc/connector/entity/living/MobEntity.java b/connector/src/main/java/org/geysermc/connector/entity/living/MobEntity.java new file mode 100644 index 000000000..3c603a61e --- /dev/null +++ b/connector/src/main/java/org/geysermc/connector/entity/living/MobEntity.java @@ -0,0 +1,66 @@ +/* + * 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.living; + +import com.github.steveice10.mc.protocol.data.game.entity.metadata.EntityMetadata; +import com.github.steveice10.mc.protocol.data.game.entity.metadata.type.ByteEntityMetadata; +import com.nukkitx.math.vector.Vector3f; +import com.nukkitx.protocol.bedrock.data.entity.EntityData; +import com.nukkitx.protocol.bedrock.data.entity.EntityFlag; +import lombok.Getter; +import org.geysermc.connector.entity.EntityDefinition; +import org.geysermc.connector.entity.LivingEntity; +import org.geysermc.connector.network.session.GeyserSession; + +import java.util.UUID; + +public class MobEntity extends LivingEntity { + /** + * If another mob is holding this mob by a leash, this variable tracks their Bedrock entity ID. + */ + @Getter + private long leashHolderBedrockId; + + public MobEntity(GeyserSession session, long 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(); + setLeashHolderBedrockId(-1); + } + + public void setMobFlags(EntityMetadata entityMetadata) { + byte xd = ((ByteEntityMetadata) entityMetadata).getPrimitiveValue(); + setFlag(EntityFlag.NO_AI, (xd & 0x01) == 0x01); + } + + public void setLeashHolderBedrockId(long bedrockId) { + this.leashHolderBedrockId = bedrockId; + dirtyMetadata.put(EntityData.LEASH_HOLDER_EID, bedrockId); + } +} diff --git a/connector/src/main/java/org/geysermc/connector/entity/living/SlimeEntity.java b/connector/src/main/java/org/geysermc/connector/entity/living/SlimeEntity.java index f08fcc796..b533a6613 100644 --- a/connector/src/main/java/org/geysermc/connector/entity/living/SlimeEntity.java +++ b/connector/src/main/java/org/geysermc/connector/entity/living/SlimeEntity.java @@ -26,22 +26,21 @@ package org.geysermc.connector.entity.living; import com.github.steveice10.mc.protocol.data.game.entity.metadata.EntityMetadata; +import com.github.steveice10.mc.protocol.data.game.entity.metadata.type.IntEntityMetadata; import com.nukkitx.math.vector.Vector3f; import com.nukkitx.protocol.bedrock.data.entity.EntityData; -import org.geysermc.connector.entity.type.EntityType; +import org.geysermc.connector.entity.EntityDefinition; import org.geysermc.connector.network.session.GeyserSession; -public class SlimeEntity extends InsentientEntity { +import java.util.UUID; - public SlimeEntity(long entityId, long geyserId, EntityType entityType, Vector3f position, Vector3f motion, Vector3f rotation) { - super(entityId, geyserId, entityType, position, motion, rotation); +public class SlimeEntity extends MobEntity { + + public SlimeEntity(GeyserSession session, long 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 - public void updateBedrockMetadata(EntityMetadata entityMetadata, GeyserSession session) { - if (entityMetadata.getId() == 16) { - this.metadata.put(EntityData.SCALE, 0.10f + (int) entityMetadata.getValue()); - } - super.updateBedrockMetadata(entityMetadata, session); + public void setScale(EntityMetadata entityMetadata) { + dirtyMetadata.put(EntityData.SCALE, 0.10f + ((IntEntityMetadata) entityMetadata).getPrimitiveValue()); } } diff --git a/connector/src/main/java/org/geysermc/connector/entity/living/SnowGolemEntity.java b/connector/src/main/java/org/geysermc/connector/entity/living/SnowGolemEntity.java index 6bfb23564..7dafd4853 100644 --- a/connector/src/main/java/org/geysermc/connector/entity/living/SnowGolemEntity.java +++ b/connector/src/main/java/org/geysermc/connector/entity/living/SnowGolemEntity.java @@ -26,24 +26,23 @@ package org.geysermc.connector.entity.living; import com.github.steveice10.mc.protocol.data.game.entity.metadata.EntityMetadata; +import com.github.steveice10.mc.protocol.data.game.entity.metadata.type.ByteEntityMetadata; import com.nukkitx.math.vector.Vector3f; import com.nukkitx.protocol.bedrock.data.entity.EntityFlag; -import org.geysermc.connector.entity.type.EntityType; +import org.geysermc.connector.entity.EntityDefinition; import org.geysermc.connector.network.session.GeyserSession; +import java.util.UUID; + public class SnowGolemEntity extends GolemEntity { - public SnowGolemEntity(long entityId, long geyserId, EntityType entityType, Vector3f position, Vector3f motion, Vector3f rotation) { - super(entityId, geyserId, entityType, position, motion, rotation); + public SnowGolemEntity(GeyserSession session, long 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 - public void updateBedrockMetadata(EntityMetadata entityMetadata, GeyserSession session) { - if (entityMetadata.getId() == 16) { - byte xd = (byte) entityMetadata.getValue(); - // Handle the visibility of the pumpkin - metadata.getFlags().setFlag(EntityFlag.SHEARED, (xd & 0x10) != 0x10); - } - super.updateBedrockMetadata(entityMetadata, session); + public void setSnowGolemFlags(EntityMetadata entityMetadata) { + byte xd = ((ByteEntityMetadata) entityMetadata).getPrimitiveValue(); + // Handle the visibility of the pumpkin + setFlag(EntityFlag.SHEARED, (xd & 0x10) != 0x10); } } diff --git a/connector/src/main/java/org/geysermc/connector/entity/living/SquidEntity.java b/connector/src/main/java/org/geysermc/connector/entity/living/SquidEntity.java index bf18b97a0..18c1ec9a8 100644 --- a/connector/src/main/java/org/geysermc/connector/entity/living/SquidEntity.java +++ b/connector/src/main/java/org/geysermc/connector/entity/living/SquidEntity.java @@ -28,28 +28,25 @@ package org.geysermc.connector.entity.living; import com.nukkitx.math.vector.Vector3f; import com.nukkitx.protocol.bedrock.data.entity.EntityFlag; import com.nukkitx.protocol.bedrock.packet.MoveEntityDeltaPacket; +import org.geysermc.connector.entity.EntityDefinition; import org.geysermc.connector.entity.Tickable; -import org.geysermc.connector.entity.type.EntityType; import org.geysermc.connector.network.session.GeyserSession; import org.geysermc.connector.network.translators.world.block.BlockStateValues; +import java.util.UUID; + public class SquidEntity extends WaterEntity implements Tickable { - - private float pitch; - private float yaw; - private float targetPitch; private float targetYaw; private boolean inWater; - public SquidEntity(long entityId, long geyserId, EntityType entityType, Vector3f position, Vector3f motion, Vector3f rotation) { - super(entityId, geyserId, entityType, position, motion, rotation); - this.yaw = rotation.getX(); + public SquidEntity(GeyserSession session, long 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 - public void tick(GeyserSession session) { + public void tick() { boolean pitchChanged; boolean yawChanged; float oldPitch = pitch; @@ -82,25 +79,33 @@ public class SquidEntity extends WaterEntity implements Tickable { } @Override - public void moveRelative(GeyserSession session, double relX, double relY, double relZ, Vector3f rotation, boolean isOnGround) { - super.moveRelative(session, relX, relY, relZ, rotation, isOnGround); - checkInWater(session); + public void moveRelative(double relX, double relY, double relZ, float yaw, float pitch, float headYaw, boolean isOnGround) { + super.moveRelative(relX, relY, relZ, yaw, pitch, headYaw, isOnGround); + checkInWater(); } @Override - public void moveAbsolute(GeyserSession session, Vector3f position, Vector3f rotation, boolean isOnGround, boolean teleported) { - super.moveAbsolute(session, position, rotation, isOnGround, teleported); - checkInWater(session); + public void moveAbsolute(Vector3f position, float yaw, float pitch, float headYaw, boolean isOnGround, boolean teleported) { + super.moveAbsolute(position, yaw, pitch, headYaw, isOnGround, teleported); + checkInWater(); } @Override - public void setRotation(Vector3f rotation) { + public void setYaw(float yaw) { // Let the Java server control yaw when the squid is out of water if (!inWater) { - yaw = rotation.getX(); + this.yaw = yaw; } } + @Override + public void setPitch(float pitch) { + } + + @Override + public void setHeadYaw(float headYaw) { + } + @Override public void setMotion(Vector3f motion) { super.setMotion(motion); @@ -115,8 +120,8 @@ public class SquidEntity extends WaterEntity implements Tickable { return Vector3f.from(pitch, yaw, yaw); } - private void checkInWater(GeyserSession session) { - if (getMetadata().getFlags().getFlag(EntityFlag.RIDING)) { + private void checkInWater() { + if (getFlag(EntityFlag.RIDING)) { inWater = false; } else { int block = session.getConnector().getWorldManager().getBlockAt(session, position.toInt()); diff --git a/connector/src/main/java/org/geysermc/connector/entity/living/WaterEntity.java b/connector/src/main/java/org/geysermc/connector/entity/living/WaterEntity.java index 9b90ba72e..4766841ac 100644 --- a/connector/src/main/java/org/geysermc/connector/entity/living/WaterEntity.java +++ b/connector/src/main/java/org/geysermc/connector/entity/living/WaterEntity.java @@ -26,11 +26,14 @@ package org.geysermc.connector.entity.living; import com.nukkitx.math.vector.Vector3f; -import org.geysermc.connector.entity.type.EntityType; +import org.geysermc.connector.entity.EntityDefinition; +import org.geysermc.connector.network.session.GeyserSession; + +import java.util.UUID; public class WaterEntity extends CreatureEntity { - public WaterEntity(long entityId, long geyserId, EntityType entityType, Vector3f position, Vector3f motion, Vector3f rotation) { - super(entityId, geyserId, entityType, position, motion, rotation); + public WaterEntity(GeyserSession session, long 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); } } diff --git a/connector/src/main/java/org/geysermc/connector/entity/living/animal/AnimalEntity.java b/connector/src/main/java/org/geysermc/connector/entity/living/animal/AnimalEntity.java index 2495eab0a..7ef8f107e 100644 --- a/connector/src/main/java/org/geysermc/connector/entity/living/animal/AnimalEntity.java +++ b/connector/src/main/java/org/geysermc/connector/entity/living/animal/AnimalEntity.java @@ -26,15 +26,17 @@ package org.geysermc.connector.entity.living.animal; import com.nukkitx.math.vector.Vector3f; +import org.geysermc.connector.entity.EntityDefinition; import org.geysermc.connector.entity.living.AgeableEntity; -import org.geysermc.connector.entity.type.EntityType; import org.geysermc.connector.network.session.GeyserSession; import org.geysermc.connector.registry.type.ItemMapping; +import java.util.UUID; + public class AnimalEntity extends AgeableEntity { - public AnimalEntity(long entityId, long geyserId, EntityType entityType, Vector3f position, Vector3f motion, Vector3f rotation) { - super(entityId, geyserId, entityType, position, motion, rotation); + public AnimalEntity(GeyserSession session, long 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); } /** diff --git a/connector/src/main/java/org/geysermc/connector/entity/living/animal/AxolotlEntity.java b/connector/src/main/java/org/geysermc/connector/entity/living/animal/AxolotlEntity.java index 0968cecb0..53c70d13e 100644 --- a/connector/src/main/java/org/geysermc/connector/entity/living/animal/AxolotlEntity.java +++ b/connector/src/main/java/org/geysermc/connector/entity/living/animal/AxolotlEntity.java @@ -26,32 +26,33 @@ package org.geysermc.connector.entity.living.animal; import com.github.steveice10.mc.protocol.data.game.entity.metadata.EntityMetadata; +import com.github.steveice10.mc.protocol.data.game.entity.metadata.type.BooleanEntityMetadata; +import com.github.steveice10.mc.protocol.data.game.entity.metadata.type.IntEntityMetadata; import com.nukkitx.math.vector.Vector3f; import com.nukkitx.protocol.bedrock.data.entity.EntityData; import com.nukkitx.protocol.bedrock.data.entity.EntityFlag; -import org.geysermc.connector.entity.type.EntityType; +import org.geysermc.connector.entity.EntityDefinition; import org.geysermc.connector.network.session.GeyserSession; import org.geysermc.connector.registry.type.ItemMapping; +import java.util.UUID; + public class AxolotlEntity extends AnimalEntity { - public AxolotlEntity(long entityId, long geyserId, EntityType entityType, Vector3f position, Vector3f motion, Vector3f rotation) { - super(entityId, geyserId, entityType, position, motion, rotation); + public AxolotlEntity(GeyserSession session, long 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 - public void updateBedrockMetadata(EntityMetadata entityMetadata, GeyserSession session) { - super.updateBedrockMetadata(entityMetadata, session); - if (entityMetadata.getId() == 17) { - int variant = (int) entityMetadata.getValue(); - switch (variant) { - case 1 -> variant = 3; // Java - "Wild" (brown) - case 3 -> variant = 1; // Java - cyan - } - metadata.put(EntityData.VARIANT, variant); - } - else if (entityMetadata.getId() == 18) { - metadata.getFlags().setFlag(EntityFlag.PLAYING_DEAD, (boolean) entityMetadata.getValue()); + public void setVariant(EntityMetadata entityMetadata) { + int variant = ((IntEntityMetadata) entityMetadata).getPrimitiveValue(); + switch (variant) { + case 1 -> variant = 3; // Java - "Wild" (brown) + case 3 -> variant = 1; // Java - cyan } + dirtyMetadata.put(EntityData.VARIANT, variant); + } + + public void setPlayingDead(EntityMetadata entityMetadata) { + setFlag(EntityFlag.PLAYING_DEAD, ((BooleanEntityMetadata) entityMetadata).getPrimitiveValue()); } @Override diff --git a/connector/src/main/java/org/geysermc/connector/entity/living/animal/BeeEntity.java b/connector/src/main/java/org/geysermc/connector/entity/living/animal/BeeEntity.java index 07136252a..12366c4cd 100644 --- a/connector/src/main/java/org/geysermc/connector/entity/living/animal/BeeEntity.java +++ b/connector/src/main/java/org/geysermc/connector/entity/living/animal/BeeEntity.java @@ -26,43 +26,44 @@ package org.geysermc.connector.entity.living.animal; import com.github.steveice10.mc.protocol.data.game.entity.metadata.EntityMetadata; +import com.github.steveice10.mc.protocol.data.game.entity.metadata.type.ByteEntityMetadata; +import com.github.steveice10.mc.protocol.data.game.entity.metadata.type.IntEntityMetadata; import com.nukkitx.math.vector.Vector3f; import com.nukkitx.protocol.bedrock.data.entity.EntityData; import com.nukkitx.protocol.bedrock.data.entity.EntityEventType; import com.nukkitx.protocol.bedrock.data.entity.EntityFlag; import com.nukkitx.protocol.bedrock.packet.EntityEventPacket; -import org.geysermc.connector.entity.type.EntityType; +import org.geysermc.connector.entity.EntityDefinition; import org.geysermc.connector.network.session.GeyserSession; import org.geysermc.connector.registry.type.ItemMapping; +import java.util.UUID; + public class BeeEntity extends AnimalEntity { - public BeeEntity(long entityId, long geyserId, EntityType entityType, Vector3f position, Vector3f motion, Vector3f rotation) { - super(entityId, geyserId, entityType, position, motion, rotation); + public BeeEntity(GeyserSession session, long 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 - public void updateBedrockMetadata(EntityMetadata entityMetadata, GeyserSession session) { - if (entityMetadata.getId() == 17) { - byte xd = (byte) entityMetadata.getValue(); - // Bee is performing sting attack; trigger animation - if ((xd & 0x02) == 0x02) { - EntityEventPacket packet = new EntityEventPacket(); - packet.setRuntimeEntityId(geyserId); - packet.setType(EntityEventType.ATTACK_START); - packet.setData(0); - session.sendUpstreamPacket(packet); - } - // If the bee has stung - metadata.put(EntityData.MARK_VARIANT, (xd & 0x04) == 0x04 ? 1 : 0); - // If the bee has nectar or not - metadata.getFlags().setFlag(EntityFlag.POWERED, (xd & 0x08) == 0x08); + public void setBeeFlags(EntityMetadata entityMetadata) { + byte xd = ((ByteEntityMetadata) entityMetadata).getPrimitiveValue(); + // Bee is performing sting attack; trigger animation + if ((xd & 0x02) == 0x02) { + EntityEventPacket packet = new EntityEventPacket(); + packet.setRuntimeEntityId(geyserId); + packet.setType(EntityEventType.ATTACK_START); + packet.setData(0); + session.sendUpstreamPacket(packet); } - if (entityMetadata.getId() == 18) { - // Converting "anger time" to a boolean - metadata.getFlags().setFlag(EntityFlag.ANGRY, (int) entityMetadata.getValue() > 0); - } - super.updateBedrockMetadata(entityMetadata, session); + // If the bee has stung + dirtyMetadata.put(EntityData.MARK_VARIANT, (xd & 0x04) == 0x04 ? 1 : 0); + // If the bee has nectar or not + setFlag(EntityFlag.POWERED, (xd & 0x08) == 0x08); + } + + public void setAngerTime(EntityMetadata entityMetadata) { + // Converting "anger time" to a boolean + setFlag(EntityFlag.ANGRY, ((IntEntityMetadata) entityMetadata).getPrimitiveValue() > 0); } @Override diff --git a/connector/src/main/java/org/geysermc/connector/entity/living/animal/ChickenEntity.java b/connector/src/main/java/org/geysermc/connector/entity/living/animal/ChickenEntity.java index c7eb62c6e..b1031209f 100644 --- a/connector/src/main/java/org/geysermc/connector/entity/living/animal/ChickenEntity.java +++ b/connector/src/main/java/org/geysermc/connector/entity/living/animal/ChickenEntity.java @@ -26,14 +26,16 @@ package org.geysermc.connector.entity.living.animal; import com.nukkitx.math.vector.Vector3f; -import org.geysermc.connector.entity.type.EntityType; +import org.geysermc.connector.entity.EntityDefinition; import org.geysermc.connector.network.session.GeyserSession; import org.geysermc.connector.registry.type.ItemMapping; +import java.util.UUID; + public class ChickenEntity extends AnimalEntity { - public ChickenEntity(long entityId, long geyserId, EntityType entityType, Vector3f position, Vector3f motion, Vector3f rotation) { - super(entityId, geyserId, entityType, position, motion, rotation); + public ChickenEntity(GeyserSession session, long 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 diff --git a/connector/src/main/java/org/geysermc/connector/entity/living/animal/FoxEntity.java b/connector/src/main/java/org/geysermc/connector/entity/living/animal/FoxEntity.java index 26da89612..7016753ba 100644 --- a/connector/src/main/java/org/geysermc/connector/entity/living/animal/FoxEntity.java +++ b/connector/src/main/java/org/geysermc/connector/entity/living/animal/FoxEntity.java @@ -26,32 +26,32 @@ package org.geysermc.connector.entity.living.animal; import com.github.steveice10.mc.protocol.data.game.entity.metadata.EntityMetadata; +import com.github.steveice10.mc.protocol.data.game.entity.metadata.type.ByteEntityMetadata; import com.nukkitx.math.vector.Vector3f; import com.nukkitx.protocol.bedrock.data.entity.EntityData; import com.nukkitx.protocol.bedrock.data.entity.EntityFlag; -import org.geysermc.connector.entity.type.EntityType; +import org.geysermc.connector.entity.EntityDefinition; import org.geysermc.connector.network.session.GeyserSession; import org.geysermc.connector.registry.type.ItemMapping; +import java.util.UUID; + public class FoxEntity extends AnimalEntity { - public FoxEntity(long entityId, long geyserId, EntityType entityType, Vector3f position, Vector3f motion, Vector3f rotation) { - super(entityId, geyserId, entityType, position, motion, rotation); + public FoxEntity(GeyserSession session, long 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 - public void updateBedrockMetadata(EntityMetadata entityMetadata, GeyserSession session) { - if (entityMetadata.getId() == 17) { - metadata.put(EntityData.VARIANT, entityMetadata.getValue()); - } - if (entityMetadata.getId() == 18) { - byte xd = (byte) entityMetadata.getValue(); - metadata.getFlags().setFlag(EntityFlag.SITTING, (xd & 0x01) == 0x01); - metadata.getFlags().setFlag(EntityFlag.SNEAKING, (xd & 0x04) == 0x04); - metadata.getFlags().setFlag(EntityFlag.INTERESTED, (xd & 0x08) == 0x08); - metadata.getFlags().setFlag(EntityFlag.SLEEPING, (xd & 0x20) == 0x20); - } - super.updateBedrockMetadata(entityMetadata, session); + public void setFoxVariant(EntityMetadata entityMetadata) { + dirtyMetadata.put(EntityData.VARIANT, entityMetadata.getValue()); + } + + public void setFoxFlags(EntityMetadata entityMetadata) { + byte xd = ((ByteEntityMetadata) entityMetadata).getPrimitiveValue(); + setFlag(EntityFlag.SITTING, (xd & 0x01) == 0x01); + setFlag(EntityFlag.SNEAKING, (xd & 0x04) == 0x04); + setFlag(EntityFlag.INTERESTED, (xd & 0x08) == 0x08); + setFlag(EntityFlag.SLEEPING, (xd & 0x20) == 0x20); } @Override diff --git a/connector/src/main/java/org/geysermc/connector/entity/living/animal/GoatEntity.java b/connector/src/main/java/org/geysermc/connector/entity/living/animal/GoatEntity.java index a43998f27..6bf7cd336 100644 --- a/connector/src/main/java/org/geysermc/connector/entity/living/animal/GoatEntity.java +++ b/connector/src/main/java/org/geysermc/connector/entity/living/animal/GoatEntity.java @@ -27,12 +27,15 @@ package org.geysermc.connector.entity.living.animal; 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.type.BooleanEntityMetadata; import com.nukkitx.math.vector.Vector3f; import com.nukkitx.protocol.bedrock.data.entity.EntityData; import lombok.Getter; -import org.geysermc.connector.entity.type.EntityType; +import org.geysermc.connector.entity.EntityDefinition; import org.geysermc.connector.network.session.GeyserSession; +import java.util.UUID; + public class GoatEntity extends AnimalEntity { private static final float LONG_JUMPING_HEIGHT = 1.3f * 0.7f; private static final float LONG_JUMPING_WIDTH = 0.9f * 0.7f; @@ -40,24 +43,20 @@ public class GoatEntity extends AnimalEntity { @Getter private boolean isScreamer; - public GoatEntity(long entityId, long geyserId, EntityType entityType, Vector3f position, Vector3f motion, Vector3f rotation) { - super(entityId, geyserId, entityType, position, motion, rotation); + public GoatEntity(GeyserSession session, long 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 - public void updateBedrockMetadata(EntityMetadata entityMetadata, GeyserSession session) { - super.updateBedrockMetadata(entityMetadata, session); - if (entityMetadata.getId() == 17) { - // Not used in Bedrock Edition - isScreamer = (boolean) entityMetadata.getValue(); - } + public void setScreamer(EntityMetadata entityMetadata) { + // Metadata not used in Bedrock Edition + isScreamer = ((BooleanEntityMetadata) entityMetadata).getPrimitiveValue(); } @Override protected void setDimensions(Pose pose) { if (pose == Pose.LONG_JUMPING) { - metadata.put(EntityData.BOUNDING_BOX_WIDTH, LONG_JUMPING_WIDTH); - metadata.put(EntityData.BOUNDING_BOX_HEIGHT, LONG_JUMPING_HEIGHT); + dirtyMetadata.put(EntityData.BOUNDING_BOX_WIDTH, LONG_JUMPING_WIDTH); + dirtyMetadata.put(EntityData.BOUNDING_BOX_HEIGHT, LONG_JUMPING_HEIGHT); } else { super.setDimensions(pose); } diff --git a/connector/src/main/java/org/geysermc/connector/entity/living/animal/HoglinEntity.java b/connector/src/main/java/org/geysermc/connector/entity/living/animal/HoglinEntity.java index d782dc53e..6493810c4 100644 --- a/connector/src/main/java/org/geysermc/connector/entity/living/animal/HoglinEntity.java +++ b/connector/src/main/java/org/geysermc/connector/entity/living/animal/HoglinEntity.java @@ -26,34 +26,32 @@ package org.geysermc.connector.entity.living.animal; import com.github.steveice10.mc.protocol.data.game.entity.metadata.EntityMetadata; +import com.github.steveice10.mc.protocol.data.game.entity.metadata.type.BooleanEntityMetadata; import com.nukkitx.math.vector.Vector3f; import com.nukkitx.protocol.bedrock.data.entity.EntityFlag; -import org.geysermc.connector.entity.type.EntityType; +import org.geysermc.connector.entity.EntityDefinition; import org.geysermc.connector.network.session.GeyserSession; import org.geysermc.connector.registry.type.ItemMapping; import org.geysermc.connector.utils.DimensionUtils; +import java.util.UUID; + public class HoglinEntity extends AnimalEntity { private boolean isImmuneToZombification; - public HoglinEntity(long entityId, long geyserId, EntityType entityType, Vector3f position, Vector3f motion, Vector3f rotation) { - super(entityId, geyserId, entityType, position, motion, rotation); + public HoglinEntity(GeyserSession session, long 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); + } + + public void setImmuneToZombification(EntityMetadata entityMetadata) { + // Apply shaking effect if not in the nether and zombification is possible + this.isImmuneToZombification = ((BooleanEntityMetadata) entityMetadata).getPrimitiveValue(); + setFlag(EntityFlag.SHAKING, isShaking()); } @Override - public void updateBedrockMetadata(EntityMetadata entityMetadata, GeyserSession session) { - if (entityMetadata.getId() == 17) { - // Immune to zombification? - // Apply shaking effect if not in the nether and zombification is possible - this.isImmuneToZombification = (boolean) entityMetadata.getValue(); - metadata.getFlags().setFlag(EntityFlag.SHAKING, isShaking(session)); - } - super.updateBedrockMetadata(entityMetadata, session); - } - - @Override - protected boolean isShaking(GeyserSession session) { - return (!isImmuneToZombification && !session.getDimension().equals(DimensionUtils.NETHER)) || super.isShaking(session); + protected boolean isShaking() { + return (!isImmuneToZombification && !session.getDimension().equals(DimensionUtils.NETHER)) || super.isShaking(); } @Override diff --git a/connector/src/main/java/org/geysermc/connector/entity/living/animal/MooshroomEntity.java b/connector/src/main/java/org/geysermc/connector/entity/living/animal/MooshroomEntity.java index e2e22c73d..2047b8afe 100644 --- a/connector/src/main/java/org/geysermc/connector/entity/living/animal/MooshroomEntity.java +++ b/connector/src/main/java/org/geysermc/connector/entity/living/animal/MooshroomEntity.java @@ -25,23 +25,15 @@ package org.geysermc.connector.entity.living.animal; -import com.github.steveice10.mc.protocol.data.game.entity.metadata.EntityMetadata; import com.nukkitx.math.vector.Vector3f; -import com.nukkitx.protocol.bedrock.data.entity.EntityData; -import org.geysermc.connector.entity.type.EntityType; +import org.geysermc.connector.entity.EntityDefinition; import org.geysermc.connector.network.session.GeyserSession; +import java.util.UUID; + public class MooshroomEntity extends AnimalEntity { - public MooshroomEntity(long entityId, long geyserId, EntityType entityType, Vector3f position, Vector3f motion, Vector3f rotation) { - super(entityId, geyserId, entityType, position, motion, rotation); - } - - @Override - public void updateBedrockMetadata(EntityMetadata entityMetadata, GeyserSession session) { - if (entityMetadata.getId() == 17) { - metadata.put(EntityData.VARIANT, entityMetadata.getValue().equals("brown") ? 1 : 0); - } - super.updateBedrockMetadata(entityMetadata, session); + public MooshroomEntity(GeyserSession session, long 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); } } diff --git a/connector/src/main/java/org/geysermc/connector/entity/living/animal/OcelotEntity.java b/connector/src/main/java/org/geysermc/connector/entity/living/animal/OcelotEntity.java index 98e7140cd..ed4f8133b 100644 --- a/connector/src/main/java/org/geysermc/connector/entity/living/animal/OcelotEntity.java +++ b/connector/src/main/java/org/geysermc/connector/entity/living/animal/OcelotEntity.java @@ -25,25 +25,17 @@ package org.geysermc.connector.entity.living.animal; -import com.github.steveice10.mc.protocol.data.game.entity.metadata.EntityMetadata; import com.nukkitx.math.vector.Vector3f; -import com.nukkitx.protocol.bedrock.data.entity.EntityFlag; -import org.geysermc.connector.entity.type.EntityType; +import org.geysermc.connector.entity.EntityDefinition; import org.geysermc.connector.network.session.GeyserSession; import org.geysermc.connector.registry.type.ItemMapping; +import java.util.UUID; + public class OcelotEntity extends AnimalEntity { - public OcelotEntity(long entityId, long geyserId, EntityType entityType, Vector3f position, Vector3f motion, Vector3f rotation) { - super(entityId, geyserId, entityType, position, motion, rotation); - } - - @Override - public void updateBedrockMetadata(EntityMetadata entityMetadata, GeyserSession session) { - if (entityMetadata.getId() == 17) { - metadata.getFlags().setFlag(EntityFlag.TRUSTING, (boolean) entityMetadata.getValue()); - } - super.updateBedrockMetadata(entityMetadata, session); + public OcelotEntity(GeyserSession session, long 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 diff --git a/connector/src/main/java/org/geysermc/connector/entity/living/animal/PandaEntity.java b/connector/src/main/java/org/geysermc/connector/entity/living/animal/PandaEntity.java index 39049d91a..ff66d30a8 100644 --- a/connector/src/main/java/org/geysermc/connector/entity/living/animal/PandaEntity.java +++ b/connector/src/main/java/org/geysermc/connector/entity/living/animal/PandaEntity.java @@ -26,57 +26,60 @@ package org.geysermc.connector.entity.living.animal; import com.github.steveice10.mc.protocol.data.game.entity.metadata.EntityMetadata; +import com.github.steveice10.mc.protocol.data.game.entity.metadata.type.ByteEntityMetadata; +import com.github.steveice10.mc.protocol.data.game.entity.metadata.type.IntEntityMetadata; import com.nukkitx.math.vector.Vector3f; import com.nukkitx.protocol.bedrock.data.entity.EntityData; import com.nukkitx.protocol.bedrock.data.entity.EntityEventType; import com.nukkitx.protocol.bedrock.data.entity.EntityFlag; import com.nukkitx.protocol.bedrock.packet.EntityEventPacket; -import org.geysermc.connector.entity.type.EntityType; +import org.geysermc.connector.entity.EntityDefinition; import org.geysermc.connector.network.session.GeyserSession; import org.geysermc.connector.registry.type.ItemMapping; -public class PandaEntity extends AnimalEntity { +import java.util.UUID; +public class PandaEntity extends AnimalEntity { private int mainGene; private int hiddenGene; - public PandaEntity(long entityId, long geyserId, EntityType entityType, Vector3f position, Vector3f motion, Vector3f rotation) { - super(entityId, geyserId, entityType, position, motion, rotation); + public PandaEntity(GeyserSession session, long 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 - public void updateBedrockMetadata(EntityMetadata entityMetadata, GeyserSession session) { - if (entityMetadata.getId() == 19) { - metadata.getFlags().setFlag(EntityFlag.EATING, (int) entityMetadata.getValue() > 0); - metadata.put(EntityData.EATING_COUNTER, entityMetadata.getValue()); - if ((int) entityMetadata.getValue() != 0) { - // Particles and sound - EntityEventPacket packet = new EntityEventPacket(); - packet.setRuntimeEntityId(geyserId); - packet.setType(EntityEventType.EATING_ITEM); - packet.setData(session.getItemMappings().getStoredItems().bamboo().getBedrockId() << 16); - session.sendUpstreamPacket(packet); - } + public void setEatingCounter(EntityMetadata entityMetadata) { + int count = ((IntEntityMetadata) entityMetadata).getPrimitiveValue(); + setFlag(EntityFlag.EATING, count > 0); + dirtyMetadata.put(EntityData.EATING_COUNTER, count); + if (count != 0) { + // Particles and sound + EntityEventPacket packet = new EntityEventPacket(); + packet.setRuntimeEntityId(geyserId); + packet.setType(EntityEventType.EATING_ITEM); + packet.setData(session.getItemMappings().getStoredItems().bamboo().getBedrockId() << 16); + session.sendUpstreamPacket(packet); } - if (entityMetadata.getId() == 20) { - mainGene = (int) (byte) entityMetadata.getValue(); - updateAppearance(); - } - if (entityMetadata.getId() == 21) { - hiddenGene = (int) (byte) entityMetadata.getValue(); - updateAppearance(); - } - if (entityMetadata.getId() == 22) { - byte xd = (byte) entityMetadata.getValue(); - metadata.getFlags().setFlag(EntityFlag.SNEEZING, (xd & 0x02) == 0x02); - metadata.getFlags().setFlag(EntityFlag.ROLLING, (xd & 0x04) == 0x04); - metadata.getFlags().setFlag(EntityFlag.SITTING, (xd & 0x08) == 0x08); - // Required to put these both for sitting to actually show - metadata.put(EntityData.SITTING_AMOUNT, (xd & 0x08) == 0x08 ? 1f : 0f); - metadata.put(EntityData.SITTING_AMOUNT_PREVIOUS, (xd & 0x08) == 0x08 ? 1f : 0f); - metadata.getFlags().setFlag(EntityFlag.LAYING_DOWN, (xd & 0x10) == 0x10); - } - super.updateBedrockMetadata(entityMetadata, session); + } + + public void setMainGene(EntityMetadata entityMetadata) { + mainGene = ((ByteEntityMetadata) entityMetadata).getPrimitiveValue(); + updateAppearance(); + } + + public void setHiddenGene(EntityMetadata entityMetadata) { + hiddenGene = ((ByteEntityMetadata) entityMetadata).getPrimitiveValue(); + updateAppearance(); + } + + public void setPandaFlags(EntityMetadata entityMetadata) { + byte xd = ((ByteEntityMetadata) entityMetadata).getPrimitiveValue(); + setFlag(EntityFlag.SNEEZING, (xd & 0x02) == 0x02); + setFlag(EntityFlag.ROLLING, (xd & 0x04) == 0x04); + setFlag(EntityFlag.SITTING, (xd & 0x08) == 0x08); + // Required to put these both for sitting to actually show + dirtyMetadata.put(EntityData.SITTING_AMOUNT, (xd & 0x08) == 0x08 ? 1f : 0f); + dirtyMetadata.put(EntityData.SITTING_AMOUNT_PREVIOUS, (xd & 0x08) == 0x08 ? 1f : 0f); + setFlag(EntityFlag.LAYING_DOWN, (xd & 0x10) == 0x10); } @Override @@ -93,14 +96,14 @@ public class PandaEntity extends AnimalEntity { // Main gene is a recessive trait if (mainGene == hiddenGene) { // Main and hidden genes match; this is what the panda looks like. - metadata.put(EntityData.VARIANT, mainGene); + dirtyMetadata.put(EntityData.VARIANT, mainGene); } else { // Genes have no effect on appearance - metadata.put(EntityData.VARIANT, 0); + dirtyMetadata.put(EntityData.VARIANT, 0); } } else { // No need to worry about hidden gene - metadata.put(EntityData.VARIANT, mainGene); + dirtyMetadata.put(EntityData.VARIANT, mainGene); } } } diff --git a/connector/src/main/java/org/geysermc/connector/entity/living/animal/PigEntity.java b/connector/src/main/java/org/geysermc/connector/entity/living/animal/PigEntity.java index ba6bfd553..8fe4fde06 100644 --- a/connector/src/main/java/org/geysermc/connector/entity/living/animal/PigEntity.java +++ b/connector/src/main/java/org/geysermc/connector/entity/living/animal/PigEntity.java @@ -25,25 +25,17 @@ package org.geysermc.connector.entity.living.animal; -import com.github.steveice10.mc.protocol.data.game.entity.metadata.EntityMetadata; import com.nukkitx.math.vector.Vector3f; -import com.nukkitx.protocol.bedrock.data.entity.EntityFlag; -import org.geysermc.connector.entity.type.EntityType; +import org.geysermc.connector.entity.EntityDefinition; import org.geysermc.connector.network.session.GeyserSession; import org.geysermc.connector.registry.type.ItemMapping; +import java.util.UUID; + public class PigEntity extends AnimalEntity { - public PigEntity(long entityId, long geyserId, EntityType entityType, Vector3f position, Vector3f motion, Vector3f rotation) { - super(entityId, geyserId, entityType, position, motion, rotation); - } - - @Override - public void updateBedrockMetadata(EntityMetadata entityMetadata, GeyserSession session) { - if (entityMetadata.getId() == 17) { - metadata.getFlags().setFlag(EntityFlag.SADDLED, (boolean) entityMetadata.getValue()); - } - super.updateBedrockMetadata(entityMetadata, session); + public PigEntity(GeyserSession session, long 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 diff --git a/connector/src/main/java/org/geysermc/connector/entity/living/animal/PolarBearEntity.java b/connector/src/main/java/org/geysermc/connector/entity/living/animal/PolarBearEntity.java index 893def315..4eb5d79fc 100644 --- a/connector/src/main/java/org/geysermc/connector/entity/living/animal/PolarBearEntity.java +++ b/connector/src/main/java/org/geysermc/connector/entity/living/animal/PolarBearEntity.java @@ -25,25 +25,17 @@ package org.geysermc.connector.entity.living.animal; -import com.github.steveice10.mc.protocol.data.game.entity.metadata.EntityMetadata; import com.nukkitx.math.vector.Vector3f; -import com.nukkitx.protocol.bedrock.data.entity.EntityFlag; -import org.geysermc.connector.entity.type.EntityType; +import org.geysermc.connector.entity.EntityDefinition; import org.geysermc.connector.network.session.GeyserSession; import org.geysermc.connector.registry.type.ItemMapping; +import java.util.UUID; + public class PolarBearEntity extends AnimalEntity { - public PolarBearEntity(long entityId, long geyserId, EntityType entityType, Vector3f position, Vector3f motion, Vector3f rotation) { - super(entityId, geyserId, entityType, position, motion, rotation); - } - - @Override - public void updateBedrockMetadata(EntityMetadata entityMetadata, GeyserSession session) { - if (entityMetadata.getId() == 17) { - metadata.getFlags().setFlag(EntityFlag.STANDING, (boolean) entityMetadata.getValue()); - } - super.updateBedrockMetadata(entityMetadata, session); + public PolarBearEntity(GeyserSession session, long 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 diff --git a/connector/src/main/java/org/geysermc/connector/entity/living/animal/PufferFishEntity.java b/connector/src/main/java/org/geysermc/connector/entity/living/animal/PufferFishEntity.java index 0ddd581f8..e2c9039de 100644 --- a/connector/src/main/java/org/geysermc/connector/entity/living/animal/PufferFishEntity.java +++ b/connector/src/main/java/org/geysermc/connector/entity/living/animal/PufferFishEntity.java @@ -26,25 +26,24 @@ package org.geysermc.connector.entity.living.animal; import com.github.steveice10.mc.protocol.data.game.entity.metadata.EntityMetadata; +import com.github.steveice10.mc.protocol.data.game.entity.metadata.type.IntEntityMetadata; import com.nukkitx.math.vector.Vector3f; import com.nukkitx.protocol.bedrock.data.entity.EntityData; +import org.geysermc.connector.entity.EntityDefinition; import org.geysermc.connector.entity.living.AbstractFishEntity; -import org.geysermc.connector.entity.type.EntityType; import org.geysermc.connector.network.session.GeyserSession; +import java.util.UUID; + public class PufferFishEntity extends AbstractFishEntity { - public PufferFishEntity(long entityId, long geyserId, EntityType entityType, Vector3f position, Vector3f motion, Vector3f rotation) { - super(entityId, geyserId, entityType, position, motion, rotation); + public PufferFishEntity(GeyserSession session, long 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 - public void updateBedrockMetadata(EntityMetadata entityMetadata, GeyserSession session) { - if (entityMetadata.getId() == 17) { - int puffsize = (int) entityMetadata.getValue(); - metadata.put(EntityData.PUFFERFISH_SIZE, (byte) puffsize); - metadata.put(EntityData.VARIANT, puffsize); - } - super.updateBedrockMetadata(entityMetadata, session); + public void setPufferfishSize(EntityMetadata entityMetadata) { + int puffsize = ((IntEntityMetadata) entityMetadata).getPrimitiveValue(); + dirtyMetadata.put(EntityData.PUFFERFISH_SIZE, (byte) puffsize); + dirtyMetadata.put(EntityData.VARIANT, puffsize); } } diff --git a/connector/src/main/java/org/geysermc/connector/entity/living/animal/RabbitEntity.java b/connector/src/main/java/org/geysermc/connector/entity/living/animal/RabbitEntity.java index 0f0e1fb3e..2d6a0d96a 100644 --- a/connector/src/main/java/org/geysermc/connector/entity/living/animal/RabbitEntity.java +++ b/connector/src/main/java/org/geysermc/connector/entity/living/animal/RabbitEntity.java @@ -26,42 +26,49 @@ package org.geysermc.connector.entity.living.animal; import com.github.steveice10.mc.protocol.data.game.entity.metadata.EntityMetadata; +import com.github.steveice10.mc.protocol.data.game.entity.metadata.type.IntEntityMetadata; import com.nukkitx.math.vector.Vector3f; import com.nukkitx.protocol.bedrock.data.entity.EntityData; import com.nukkitx.protocol.bedrock.data.entity.EntityFlag; -import org.geysermc.connector.entity.type.EntityType; +import org.geysermc.connector.entity.EntityDefinition; import org.geysermc.connector.network.session.GeyserSession; import org.geysermc.connector.registry.type.ItemMapping; +import java.util.UUID; + public class RabbitEntity extends AnimalEntity { - public RabbitEntity(long entityId, long geyserId, EntityType entityType, Vector3f position, Vector3f motion, Vector3f rotation) { - super(entityId, geyserId, entityType, position, motion, rotation); + public RabbitEntity(GeyserSession session, long 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 - public void updateBedrockMetadata(EntityMetadata entityMetadata, GeyserSession session) { - super.updateBedrockMetadata(entityMetadata, session); - if (entityMetadata.getId() == 16) { - metadata.put(EntityData.SCALE, .55f); - boolean isBaby = (boolean) entityMetadata.getValue(); - if (isBaby) { - metadata.put(EntityData.SCALE, .35f); - metadata.getFlags().setFlag(EntityFlag.BABY, true); - } - } else if (entityMetadata.getId() == 17) { - int variant = (int) entityMetadata.getValue(); + public void setBaby(EntityMetadata entityMetadata) { + super.setBaby(entityMetadata); + } - // Change the killer bunny to display as white since it only exists on Java Edition - boolean isKillerBunny = variant == 99; - if (isKillerBunny) { - variant = 1; - } - // Allow the resource pack to adjust to the killer bunny - metadata.getFlags().setFlag(EntityFlag.BRIBED, isKillerBunny); + public void setRabbitVariant(EntityMetadata entityMetadata) { + int variant = ((IntEntityMetadata) entityMetadata).getPrimitiveValue(); - metadata.put(EntityData.VARIANT, variant); + // Change the killer bunny to display as white since it only exists on Java Edition + boolean isKillerBunny = variant == 99; + if (isKillerBunny) { + variant = 1; } + // Allow the resource pack to adjust to the killer bunny + setFlag(EntityFlag.BRIBED, isKillerBunny); + + dirtyMetadata.put(EntityData.VARIANT, variant); + } + + @Override + protected float getAdultSize() { + return 0.55f; + } + + @Override + protected float getBabySize() { + return 0.35f; } @Override diff --git a/connector/src/main/java/org/geysermc/connector/entity/living/animal/SheepEntity.java b/connector/src/main/java/org/geysermc/connector/entity/living/animal/SheepEntity.java index f723eff2b..c063188c5 100644 --- a/connector/src/main/java/org/geysermc/connector/entity/living/animal/SheepEntity.java +++ b/connector/src/main/java/org/geysermc/connector/entity/living/animal/SheepEntity.java @@ -26,26 +26,24 @@ package org.geysermc.connector.entity.living.animal; import com.github.steveice10.mc.protocol.data.game.entity.metadata.EntityMetadata; +import com.github.steveice10.mc.protocol.data.game.entity.metadata.type.ByteEntityMetadata; import com.nukkitx.math.vector.Vector3f; import com.nukkitx.protocol.bedrock.data.entity.EntityData; import com.nukkitx.protocol.bedrock.data.entity.EntityFlag; -import org.geysermc.connector.entity.type.EntityType; +import org.geysermc.connector.entity.EntityDefinition; import org.geysermc.connector.network.session.GeyserSession; +import java.util.UUID; + public class SheepEntity extends AnimalEntity { - public SheepEntity(long entityId, long geyserId, EntityType entityType, Vector3f position, Vector3f motion, Vector3f rotation) { - super(entityId, geyserId, entityType, position, motion, rotation); + public SheepEntity(GeyserSession session, long 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 - public void updateBedrockMetadata(EntityMetadata entityMetadata, GeyserSession session) { - if (entityMetadata.getId() == 17) { - byte xd = (byte) entityMetadata.getValue(); - metadata.getFlags().setFlag(EntityFlag.SHEARED, (xd & 0x10) == 0x10); - metadata.put(EntityData.COLOR, xd); - } - - super.updateBedrockMetadata(entityMetadata, session); + public void setSheepFlags(EntityMetadata entityMetadata) { + byte xd = ((ByteEntityMetadata) entityMetadata).getPrimitiveValue(); + setFlag(EntityFlag.SHEARED, (xd & 0x10) == 0x10); + dirtyMetadata.put(EntityData.COLOR, xd); } } \ No newline at end of file diff --git a/connector/src/main/java/org/geysermc/connector/entity/living/animal/StriderEntity.java b/connector/src/main/java/org/geysermc/connector/entity/living/animal/StriderEntity.java index 83c1e3674..d31bf4a4e 100644 --- a/connector/src/main/java/org/geysermc/connector/entity/living/animal/StriderEntity.java +++ b/connector/src/main/java/org/geysermc/connector/entity/living/animal/StriderEntity.java @@ -26,70 +26,70 @@ package org.geysermc.connector.entity.living.animal; import com.github.steveice10.mc.protocol.data.game.entity.metadata.EntityMetadata; +import com.github.steveice10.mc.protocol.data.game.entity.metadata.type.BooleanEntityMetadata; import com.nukkitx.math.vector.Vector3f; import com.nukkitx.protocol.bedrock.data.entity.EntityFlag; import org.geysermc.connector.entity.Entity; -import org.geysermc.connector.entity.type.EntityType; +import org.geysermc.connector.entity.EntityDefinition; import org.geysermc.connector.network.session.GeyserSession; import org.geysermc.connector.registry.type.ItemMapping; +import java.util.UUID; + public class StriderEntity extends AnimalEntity { private boolean isCold = false; - public StriderEntity(long entityId, long geyserId, EntityType entityType, Vector3f position, Vector3f motion, Vector3f rotation) { - super(entityId, geyserId, entityType, position, motion, rotation); + public StriderEntity(GeyserSession session, long 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); - metadata.getFlags().setFlag(EntityFlag.FIRE_IMMUNE, true); - metadata.getFlags().setFlag(EntityFlag.BREATHING, true); + setFlag(EntityFlag.FIRE_IMMUNE, true); + setFlag(EntityFlag.BREATHING, true); + } + + public void setCold(EntityMetadata entityMetadata) { + isCold = ((BooleanEntityMetadata) entityMetadata).getPrimitiveValue(); + } + + public void setSaddled(EntityMetadata entityMetadata) { + setFlag(EntityFlag.SADDLED, ((BooleanEntityMetadata) entityMetadata).getPrimitiveValue()); } @Override - public void updateBedrockMetadata(EntityMetadata entityMetadata, GeyserSession session) { - if (entityMetadata.getId() == 18) { - isCold = (boolean) entityMetadata.getValue(); - } - if (entityMetadata.getId() == 19) { - metadata.getFlags().setFlag(EntityFlag.SADDLED, (boolean) entityMetadata.getValue()); - } - - super.updateBedrockMetadata(entityMetadata, session); - } - - @Override - public void updateBedrockMetadata(GeyserSession session) { + public void updateBedrockMetadata() { // Make sure they are not shaking when riding another entity // Needs to copy the parent state - if (metadata.getFlags().getFlag(EntityFlag.RIDING)) { + if (getFlag(EntityFlag.RIDING)) { boolean parentShaking = false; + //TODO optimize for (Entity ent : session.getEntityCache().getEntities().values()) { if (ent.getPassengers().contains(entityId) && ent instanceof StriderEntity) { - parentShaking = ent.getMetadata().getFlags().getFlag(EntityFlag.SHAKING); + parentShaking = ent.getFlag(EntityFlag.SHAKING); break; } } - metadata.getFlags().setFlag(EntityFlag.BREATHING, !parentShaking); - metadata.getFlags().setFlag(EntityFlag.SHAKING, parentShaking); + setFlag(EntityFlag.BREATHING, !parentShaking); + setFlag(EntityFlag.SHAKING, parentShaking); } else { - metadata.getFlags().setFlag(EntityFlag.BREATHING, !isCold); - metadata.getFlags().setFlag(EntityFlag.SHAKING, isShaking(session)); + setFlag(EntityFlag.BREATHING, !isCold); + setFlag(EntityFlag.SHAKING, isShaking()); } // Update the passengers if we have any for (long passenger : passengers) { Entity passengerEntity = session.getEntityCache().getEntityByJavaId(passenger); if (passengerEntity != null) { - passengerEntity.updateBedrockMetadata(session); + passengerEntity.updateBedrockMetadata(); } } - super.updateBedrockMetadata(session); + super.updateBedrockMetadata(); } @Override - protected boolean isShaking(GeyserSession session) { - return isCold || super.isShaking(session); + protected boolean isShaking() { + return isCold || super.isShaking(); } @Override diff --git a/connector/src/main/java/org/geysermc/connector/entity/living/animal/TropicalFishEntity.java b/connector/src/main/java/org/geysermc/connector/entity/living/animal/TropicalFishEntity.java index d16eb2ece..cd652f6a3 100644 --- a/connector/src/main/java/org/geysermc/connector/entity/living/animal/TropicalFishEntity.java +++ b/connector/src/main/java/org/geysermc/connector/entity/living/animal/TropicalFishEntity.java @@ -26,15 +26,17 @@ package org.geysermc.connector.entity.living.animal; import com.github.steveice10.mc.protocol.data.game.entity.metadata.EntityMetadata; +import com.github.steveice10.mc.protocol.data.game.entity.metadata.type.IntEntityMetadata; import com.google.common.collect.ImmutableList; import com.nukkitx.math.vector.Vector3f; import com.nukkitx.protocol.bedrock.data.entity.EntityData; import it.unimi.dsi.fastutil.ints.IntList; +import org.geysermc.connector.entity.EntityDefinition; import org.geysermc.connector.entity.living.AbstractFishEntity; -import org.geysermc.connector.entity.type.EntityType; import org.geysermc.connector.network.session.GeyserSession; import java.util.List; +import java.util.UUID; public class TropicalFishEntity extends AbstractFishEntity { @@ -47,21 +49,17 @@ public class TropicalFishEntity extends AbstractFishEntity { private static final List VARIANT_NAMES = ImmutableList.of("kob", "sunstreak", "snooper", "dasher", "brinely", "spotty", "flopper", "stripey", "glitter", "blockfish", "betty", "clayfish"); private static final List COLOR_NAMES = ImmutableList.of("white", "orange", "magenta", "light_blue", "yellow", "lime", "pink", "gray", "light_gray", "cyan", "purple", "blue", "brown", "green", "red", "black"); - public TropicalFishEntity(long entityId, long geyserId, EntityType entityType, Vector3f position, Vector3f motion, Vector3f rotation) { - super(entityId, geyserId, entityType, position, motion, rotation); + public TropicalFishEntity(GeyserSession session, long 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 - public void updateBedrockMetadata(EntityMetadata entityMetadata, GeyserSession session) { - if (entityMetadata.getId() == 17) { - int varNumber = (int) entityMetadata.getValue(); + public void setFishVariant(EntityMetadata entityMetadata) { + int varNumber = ((IntEntityMetadata) entityMetadata).getPrimitiveValue(); - metadata.put(EntityData.VARIANT, getShape(varNumber)); // Shape 0-1 - metadata.put(EntityData.MARK_VARIANT, getPattern(varNumber)); // Pattern 0-5 - metadata.put(EntityData.COLOR, getBaseColor(varNumber)); // Base color 0-15 - metadata.put(EntityData.COLOR_2, getPatternColor(varNumber)); // Pattern color 0-15 - } - super.updateBedrockMetadata(entityMetadata, session); + dirtyMetadata.put(EntityData.VARIANT, getShape(varNumber)); // Shape 0-1 + dirtyMetadata.put(EntityData.MARK_VARIANT, getPattern(varNumber)); // Pattern 0-5 + dirtyMetadata.put(EntityData.COLOR, getBaseColor(varNumber)); // Base color 0-15 + dirtyMetadata.put(EntityData.COLOR_2, getPatternColor(varNumber)); // Pattern color 0-15 } public static int getShape(int variant) { diff --git a/connector/src/main/java/org/geysermc/connector/entity/living/animal/TurtleEntity.java b/connector/src/main/java/org/geysermc/connector/entity/living/animal/TurtleEntity.java index b26b21ee2..28ff2d836 100644 --- a/connector/src/main/java/org/geysermc/connector/entity/living/animal/TurtleEntity.java +++ b/connector/src/main/java/org/geysermc/connector/entity/living/animal/TurtleEntity.java @@ -26,26 +26,27 @@ package org.geysermc.connector.entity.living.animal; import com.github.steveice10.mc.protocol.data.game.entity.metadata.EntityMetadata; +import com.github.steveice10.mc.protocol.data.game.entity.metadata.type.BooleanEntityMetadata; import com.nukkitx.math.vector.Vector3f; import com.nukkitx.protocol.bedrock.data.entity.EntityFlag; -import org.geysermc.connector.entity.type.EntityType; +import org.geysermc.connector.entity.EntityDefinition; import org.geysermc.connector.network.session.GeyserSession; import org.geysermc.connector.registry.type.ItemMapping; +import java.util.UUID; + public class TurtleEntity extends AnimalEntity { - public TurtleEntity(long entityId, long geyserId, EntityType entityType, Vector3f position, Vector3f motion, Vector3f rotation) { - super(entityId, geyserId, entityType, position, motion, rotation); + public TurtleEntity(GeyserSession session, long 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 - public void updateBedrockMetadata(EntityMetadata entityMetadata, GeyserSession session) { - if (entityMetadata.getId() == 18) { - metadata.getFlags().setFlag(EntityFlag.IS_PREGNANT, (boolean) entityMetadata.getValue()); - } else if (entityMetadata.getId() == 19) { - metadata.getFlags().setFlag(EntityFlag.LAYING_EGG, (boolean) entityMetadata.getValue()); - } - super.updateBedrockMetadata(entityMetadata, session); + public void setPregnant(EntityMetadata entityMetadata) { + setFlag(EntityFlag.IS_PREGNANT, ((BooleanEntityMetadata) entityMetadata).getPrimitiveValue()); + } + + public void setLayingEgg(EntityMetadata entityMetadata) { + setFlag(EntityFlag.LAYING_EGG, ((BooleanEntityMetadata) entityMetadata).getPrimitiveValue()); } @Override diff --git a/connector/src/main/java/org/geysermc/connector/entity/living/animal/horse/AbstractHorseEntity.java b/connector/src/main/java/org/geysermc/connector/entity/living/animal/horse/AbstractHorseEntity.java index e255d2856..27a359d63 100644 --- a/connector/src/main/java/org/geysermc/connector/entity/living/animal/horse/AbstractHorseEntity.java +++ b/connector/src/main/java/org/geysermc/connector/entity/living/animal/horse/AbstractHorseEntity.java @@ -26,6 +26,7 @@ package org.geysermc.connector.entity.living.animal.horse; import com.github.steveice10.mc.protocol.data.game.entity.metadata.EntityMetadata; +import com.github.steveice10.mc.protocol.data.game.entity.metadata.type.ByteEntityMetadata; import com.google.common.collect.ImmutableSet; import com.nukkitx.math.vector.Vector3f; import com.nukkitx.protocol.bedrock.data.entity.EntityData; @@ -34,13 +35,14 @@ import com.nukkitx.protocol.bedrock.data.entity.EntityFlag; import com.nukkitx.protocol.bedrock.data.inventory.ContainerType; import com.nukkitx.protocol.bedrock.packet.EntityEventPacket; import com.nukkitx.protocol.bedrock.packet.UpdateAttributesPacket; +import org.geysermc.connector.entity.EntityDefinition; 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; import org.geysermc.connector.registry.type.ItemMapping; import java.util.Set; +import java.util.UUID; public class AbstractHorseEntity extends AnimalEntity { /** @@ -50,18 +52,22 @@ public class AbstractHorseEntity extends AnimalEntity { private static final Set DONKEY_AND_HORSE_FOODS = ImmutableSet.of("golden_apple", "enchanted_golden_apple", "golden_carrot", "sugar", "apple", "wheat", "hay_block"); - public AbstractHorseEntity(long entityId, long geyserId, EntityType entityType, Vector3f position, Vector3f motion, Vector3f rotation) { - super(entityId, geyserId, entityType, position, motion, rotation); + public AbstractHorseEntity(GeyserSession session, long 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); // Specifies the size of the entity's inventory. Required to place slots in the entity. - metadata.put(EntityData.CONTAINER_BASE_SIZE, 2); + dirtyMetadata.put(EntityData.CONTAINER_BASE_SIZE, getContainerBaseSize()); - metadata.getFlags().setFlag(EntityFlag.WASD_CONTROLLED, true); + setFlag(EntityFlag.WASD_CONTROLLED, true); + } + + protected int getContainerBaseSize() { + return 2; } @Override - public void spawnEntity(GeyserSession session) { - super.spawnEntity(session); + public void spawnEntity() { + super.spawnEntity(); // 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 @@ -73,49 +79,44 @@ public class AbstractHorseEntity extends AnimalEntity { session.sendUpstreamPacket(attributesPacket); } - @Override - public void updateBedrockMetadata(EntityMetadata entityMetadata, GeyserSession session) { - if (entityMetadata.getId() == 17) { - byte xd = (byte) entityMetadata.getValue(); - boolean tamed = (xd & 0x02) == 0x02; - boolean saddled = (xd & 0x04) == 0x04; - metadata.getFlags().setFlag(EntityFlag.TAMED, tamed); - metadata.getFlags().setFlag(EntityFlag.SADDLED, saddled); - metadata.getFlags().setFlag(EntityFlag.EATING, (xd & 0x10) == 0x10); - metadata.getFlags().setFlag(EntityFlag.STANDING, (xd & 0x20) == 0x20); + public void setHorseFlags(EntityMetadata entityMetadata) { + byte xd = ((ByteEntityMetadata) entityMetadata).getPrimitiveValue(); + boolean tamed = (xd & 0x02) == 0x02; + boolean saddled = (xd & 0x04) == 0x04; + setFlag(EntityFlag.TAMED, tamed); + setFlag(EntityFlag.SADDLED, saddled); + setFlag(EntityFlag.EATING, (xd & 0x10) == 0x10); + setFlag(EntityFlag.STANDING, (xd & 0x20) == 0x20); - // HorseFlags - // Bred 0x10 - // Eating 0x20 - // Open mouth 0x80 - int horseFlags = 0x0; - horseFlags = (xd & 0x40) == 0x40 ? horseFlags | 0x80 : horseFlags; + // HorseFlags + // Bred 0x10 + // Eating 0x20 + // Open mouth 0x80 + int horseFlags = 0x0; + horseFlags = (xd & 0x40) == 0x40 ? horseFlags | 0x80 : horseFlags; - // Only set eating when we don't have mouth open so a player interaction doesn't trigger the eating animation - horseFlags = (xd & 0x10) == 0x10 && (xd & 0x40) != 0x40 ? horseFlags | 0x20 : horseFlags; + // Only set eating when we don't have mouth open so a player interaction doesn't trigger the eating animation + horseFlags = (xd & 0x10) == 0x10 && (xd & 0x40) != 0x40 ? horseFlags | 0x20 : horseFlags; - // Set the flags into the display item - metadata.put(EntityData.DISPLAY_ITEM, horseFlags); + // Set the flags into the display item + dirtyMetadata.put(EntityData.DISPLAY_ITEM, horseFlags); - // Send the eating particles - // We use the wheat metadata as static particles since Java - // doesn't send over what item was used to feed the horse - if ((xd & 0x40) == 0x40) { - EntityEventPacket entityEventPacket = new EntityEventPacket(); - entityEventPacket.setRuntimeEntityId(geyserId); - entityEventPacket.setType(EntityEventType.EATING_ITEM); - entityEventPacket.setData(session.getItemMappings().getStoredItems().wheat().getBedrockId() << 16); - session.sendUpstreamPacket(entityEventPacket); - } - - // Set container type if tamed - metadata.put(EntityData.CONTAINER_TYPE, tamed ? (byte) ContainerType.HORSE.getId() : (byte) 0); - - // Shows the jump meter - metadata.getFlags().setFlag(EntityFlag.CAN_POWER_JUMP, saddled); + // Send the eating particles + // We use the wheat metadata as static particles since Java + // doesn't send over what item was used to feed the horse + if ((xd & 0x40) == 0x40) { + EntityEventPacket entityEventPacket = new EntityEventPacket(); + entityEventPacket.setRuntimeEntityId(geyserId); + entityEventPacket.setType(EntityEventType.EATING_ITEM); + entityEventPacket.setData(session.getItemMappings().getStoredItems().wheat().getBedrockId() << 16); + session.sendUpstreamPacket(entityEventPacket); } - super.updateBedrockMetadata(entityMetadata, session); + // Set container type if tamed + dirtyMetadata.put(EntityData.CONTAINER_TYPE, tamed ? (byte) ContainerType.HORSE.getId() : (byte) 0); + + // Shows the jump meter + setFlag(EntityFlag.CAN_POWER_JUMP, saddled); } @Override diff --git a/connector/src/main/java/org/geysermc/connector/entity/living/animal/horse/ChestedHorseEntity.java b/connector/src/main/java/org/geysermc/connector/entity/living/animal/horse/ChestedHorseEntity.java index e4f0cc241..5ccbb4ea7 100644 --- a/connector/src/main/java/org/geysermc/connector/entity/living/animal/horse/ChestedHorseEntity.java +++ b/connector/src/main/java/org/geysermc/connector/entity/living/animal/horse/ChestedHorseEntity.java @@ -25,26 +25,20 @@ package org.geysermc.connector.entity.living.animal.horse; -import com.github.steveice10.mc.protocol.data.game.entity.metadata.EntityMetadata; import com.nukkitx.math.vector.Vector3f; -import com.nukkitx.protocol.bedrock.data.entity.EntityData; -import com.nukkitx.protocol.bedrock.data.entity.EntityFlag; -import org.geysermc.connector.entity.type.EntityType; +import org.geysermc.connector.entity.EntityDefinition; import org.geysermc.connector.network.session.GeyserSession; +import java.util.UUID; + public class ChestedHorseEntity extends AbstractHorseEntity { - public ChestedHorseEntity(long entityId, long geyserId, EntityType entityType, Vector3f position, Vector3f motion, Vector3f rotation) { - super(entityId, geyserId, entityType, position, motion, rotation); - - metadata.put(EntityData.CONTAINER_BASE_SIZE, 16); + public ChestedHorseEntity(GeyserSession session, long 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 - public void updateBedrockMetadata(EntityMetadata entityMetadata, GeyserSession session) { - if (entityMetadata.getId() == 19) { - metadata.getFlags().setFlag(EntityFlag.CHESTED, (boolean) entityMetadata.getValue()); - } - super.updateBedrockMetadata(entityMetadata, session); + protected int getContainerBaseSize() { + return 16; } } diff --git a/connector/src/main/java/org/geysermc/connector/entity/living/animal/horse/HorseEntity.java b/connector/src/main/java/org/geysermc/connector/entity/living/animal/horse/HorseEntity.java index 87155005d..97354d047 100644 --- a/connector/src/main/java/org/geysermc/connector/entity/living/animal/horse/HorseEntity.java +++ b/connector/src/main/java/org/geysermc/connector/entity/living/animal/horse/HorseEntity.java @@ -26,24 +26,23 @@ package org.geysermc.connector.entity.living.animal.horse; import com.github.steveice10.mc.protocol.data.game.entity.metadata.EntityMetadata; +import com.github.steveice10.mc.protocol.data.game.entity.metadata.type.IntEntityMetadata; import com.nukkitx.math.vector.Vector3f; import com.nukkitx.protocol.bedrock.data.entity.EntityData; -import org.geysermc.connector.entity.type.EntityType; +import org.geysermc.connector.entity.EntityDefinition; import org.geysermc.connector.network.session.GeyserSession; +import java.util.UUID; + public class HorseEntity extends AbstractHorseEntity { - public HorseEntity(long entityId, long geyserId, EntityType entityType, Vector3f position, Vector3f motion, Vector3f rotation) { - super(entityId, geyserId, entityType, position, motion, rotation); + public HorseEntity(GeyserSession session, long 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 - public void updateBedrockMetadata(EntityMetadata entityMetadata, GeyserSession session) { - if (entityMetadata.getId() == 19) { - metadata.put(EntityData.VARIANT, ((int) entityMetadata.getValue()) & 255); - metadata.put(EntityData.MARK_VARIANT, (((int) entityMetadata.getValue()) >> 8) % 5); - } - super.updateBedrockMetadata(entityMetadata, session); + public void setHorseVariant(EntityMetadata entityMetadata) { + int value = ((IntEntityMetadata) entityMetadata).getPrimitiveValue(); + dirtyMetadata.put(EntityData.VARIANT, value & 255); + dirtyMetadata.put(EntityData.MARK_VARIANT, (value >> 8) % 5); } - } \ No newline at end of file diff --git a/connector/src/main/java/org/geysermc/connector/entity/living/animal/horse/LlamaEntity.java b/connector/src/main/java/org/geysermc/connector/entity/living/animal/horse/LlamaEntity.java index 14e625c81..1c4e5155e 100644 --- a/connector/src/main/java/org/geysermc/connector/entity/living/animal/horse/LlamaEntity.java +++ b/connector/src/main/java/org/geysermc/connector/entity/living/animal/horse/LlamaEntity.java @@ -26,54 +26,47 @@ package org.geysermc.connector.entity.living.animal.horse; import com.github.steveice10.mc.protocol.data.game.entity.metadata.EntityMetadata; +import com.github.steveice10.mc.protocol.data.game.entity.metadata.type.IntEntityMetadata; import com.nukkitx.math.vector.Vector3f; import com.nukkitx.protocol.bedrock.data.entity.EntityData; import com.nukkitx.protocol.bedrock.data.inventory.ItemData; import com.nukkitx.protocol.bedrock.packet.MobArmorEquipmentPacket; -import org.geysermc.connector.entity.type.EntityType; +import org.geysermc.connector.entity.EntityDefinition; import org.geysermc.connector.network.session.GeyserSession; import org.geysermc.connector.registry.type.ItemMapping; +import java.util.UUID; + public class LlamaEntity extends ChestedHorseEntity { - public LlamaEntity(long entityId, long geyserId, EntityType entityType, Vector3f position, Vector3f motion, Vector3f rotation) { - super(entityId, geyserId, entityType, position, motion, rotation); + public LlamaEntity(GeyserSession session, long 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); - metadata.put(EntityData.CONTAINER_STRENGTH_MODIFIER, 3); // Presumably 3 slots for every 1 strength + dirtyMetadata.put(EntityData.CONTAINER_STRENGTH_MODIFIER, 3); // Presumably 3 slots for every 1 strength } - @Override - public void updateBedrockMetadata(EntityMetadata entityMetadata, GeyserSession session) { - // Strength - if (entityMetadata.getId() == 20) { - metadata.put(EntityData.STRENGTH, entityMetadata.getValue()); + /** + * Color equipped on the llama + */ + public void setCarpetedColor(EntityMetadata entityMetadata) { + // Bedrock treats llama decoration as armor + MobArmorEquipmentPacket equipmentPacket = new MobArmorEquipmentPacket(); + equipmentPacket.setRuntimeEntityId(geyserId); + // -1 means no armor + int carpetIndex = ((IntEntityMetadata) entityMetadata).getPrimitiveValue(); + if (carpetIndex > -1 && carpetIndex <= 15) { + // The damage value is the dye color that Java sends us, for pre-1.16.220 + // The item is always going to be a carpet + equipmentPacket.setChestplate(session.getItemMappings().getCarpets().get(carpetIndex)); + } else { + equipmentPacket.setChestplate(ItemData.AIR); } - // Color equipped on the llama - if (entityMetadata.getId() == 21) { - // Bedrock treats llama decoration as armor - MobArmorEquipmentPacket equipmentPacket = new MobArmorEquipmentPacket(); - equipmentPacket.setRuntimeEntityId(geyserId); - // -1 means no armor - int carpetIndex = (int) entityMetadata.getValue(); - if (carpetIndex > -1 && carpetIndex <= 15) { - // The damage value is the dye color that Java sends us, for pre-1.16.220 - // The item is always going to be a carpet - equipmentPacket.setChestplate(session.getItemMappings().getCarpets().get(carpetIndex)); - } else { - equipmentPacket.setChestplate(ItemData.AIR); - } - // Required to fill out the rest of the equipment or Bedrock ignores it, including above else statement if removing armor - equipmentPacket.setBoots(ItemData.AIR); - equipmentPacket.setHelmet(ItemData.AIR); - equipmentPacket.setLeggings(ItemData.AIR); + // Required to fill out the rest of the equipment or Bedrock ignores it, including above else statement if removing armor + equipmentPacket.setBoots(ItemData.AIR); + equipmentPacket.setHelmet(ItemData.AIR); + equipmentPacket.setLeggings(ItemData.AIR); - session.sendUpstreamPacket(equipmentPacket); - } - // Color of the llama - if (entityMetadata.getId() == 22) { - metadata.put(EntityData.VARIANT, entityMetadata.getValue()); - } - super.updateBedrockMetadata(entityMetadata, session); + session.sendUpstreamPacket(equipmentPacket); } @Override diff --git a/connector/src/main/java/org/geysermc/connector/entity/living/animal/horse/TraderLlamaEntity.java b/connector/src/main/java/org/geysermc/connector/entity/living/animal/horse/TraderLlamaEntity.java index 77059ae35..b8bfcdca6 100644 --- a/connector/src/main/java/org/geysermc/connector/entity/living/animal/horse/TraderLlamaEntity.java +++ b/connector/src/main/java/org/geysermc/connector/entity/living/animal/horse/TraderLlamaEntity.java @@ -27,18 +27,20 @@ package org.geysermc.connector.entity.living.animal.horse; import com.nukkitx.math.vector.Vector3f; import com.nukkitx.protocol.bedrock.data.entity.EntityData; -import org.geysermc.connector.entity.type.EntityType; +import org.geysermc.connector.entity.EntityDefinition; import org.geysermc.connector.network.session.GeyserSession; +import java.util.UUID; + public class TraderLlamaEntity extends LlamaEntity { - public TraderLlamaEntity(long entityId, long geyserId, EntityType entityType, Vector3f position, Vector3f motion, Vector3f rotation) { - super(entityId, geyserId, entityType, position, motion, rotation); + public TraderLlamaEntity(GeyserSession session, long 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 - public void spawnEntity(GeyserSession session) { - this.metadata.put(EntityData.MARK_VARIANT, 1); - super.spawnEntity(session); + protected void initializeMetadata() { + super.initializeMetadata(); + this.dirtyMetadata.put(EntityData.MARK_VARIANT, 1); } } diff --git a/connector/src/main/java/org/geysermc/connector/entity/living/animal/tameable/CatEntity.java b/connector/src/main/java/org/geysermc/connector/entity/living/animal/tameable/CatEntity.java index f22124409..a4fe27535 100644 --- a/connector/src/main/java/org/geysermc/connector/entity/living/animal/tameable/CatEntity.java +++ b/connector/src/main/java/org/geysermc/connector/entity/living/animal/tameable/CatEntity.java @@ -26,58 +26,71 @@ package org.geysermc.connector.entity.living.animal.tameable; import com.github.steveice10.mc.protocol.data.game.entity.metadata.EntityMetadata; +import com.github.steveice10.mc.protocol.data.game.entity.metadata.type.BooleanEntityMetadata; +import com.github.steveice10.mc.protocol.data.game.entity.metadata.type.IntEntityMetadata; import com.nukkitx.math.vector.Vector3f; import com.nukkitx.protocol.bedrock.data.entity.EntityData; import com.nukkitx.protocol.bedrock.data.entity.EntityFlag; -import org.geysermc.connector.entity.type.EntityType; +import org.geysermc.connector.entity.EntityDefinition; import org.geysermc.connector.network.session.GeyserSession; import org.geysermc.connector.registry.type.ItemMapping; +import java.util.UUID; + public class CatEntity extends TameableEntity { private byte collarColor; - public CatEntity(long entityId, long geyserId, EntityType entityType, Vector3f position, Vector3f motion, Vector3f rotation) { - super(entityId, geyserId, entityType, position, motion, rotation); + public CatEntity(GeyserSession session, long 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 - public void updateRotation(GeyserSession session, float yaw, float pitch, boolean isOnGround) { - moveRelative(session, 0, 0, 0, Vector3f.from(this.rotation.getX(), pitch, yaw), isOnGround); + public void updateRotation(float yaw, float pitch, boolean isOnGround) { + moveRelative(0, 0, 0, yaw, pitch, yaw, isOnGround); } @Override - public void updateBedrockMetadata(EntityMetadata entityMetadata, GeyserSession session) { - super.updateBedrockMetadata(entityMetadata, session); - if (entityMetadata.getId() == 16) { - metadata.put(EntityData.SCALE, (boolean) entityMetadata.getValue() ? 0.4f : 0.8f); - } else if (entityMetadata.getId() == 17) { - // Update collar color if tamed - if (metadata.getFlags().getFlag(EntityFlag.TAMED)) { - metadata.put(EntityData.COLOR, collarColor); - } + protected float getAdultSize() { + return 0.8f; + } + + @Override + protected float getBabySize() { + return 0.4f; + } + + @Override + public void setTameableFlags(EntityMetadata entityMetadata) { + super.setTameableFlags(entityMetadata); + // Update collar color if tamed + if (getFlag(EntityFlag.TAMED)) { + dirtyMetadata.put(EntityData.COLOR, collarColor); } - if (entityMetadata.getId() == 19) { - // Different colors in Java and Bedrock for some reason - int metadataValue = (int) entityMetadata.getValue(); - int variantColor = switch (metadataValue) { - case 0 -> 8; - case 8 -> 0; - case 9 -> 10; - case 10 -> 9; - default -> metadataValue; - }; - metadata.put(EntityData.VARIANT, variantColor); - } - if (entityMetadata.getId() == 20) { - metadata.getFlags().setFlag(EntityFlag.RESTING, (boolean) entityMetadata.getValue()); - } - if (entityMetadata.getId() == 22) { - collarColor = (byte) (int) entityMetadata.getValue(); - // Needed or else wild cats are a red color - if (metadata.getFlags().getFlag(EntityFlag.TAMED)) { - metadata.put(EntityData.COLOR, collarColor); - } + } + + public void setCatVariant(EntityMetadata entityMetadata) { + // Different colors in Java and Bedrock for some reason + int metadataValue = ((IntEntityMetadata) entityMetadata).getPrimitiveValue(); + int variantColor = switch (metadataValue) { + case 0 -> 8; + case 8 -> 0; + case 9 -> 10; + case 10 -> 9; + default -> metadataValue; + }; + dirtyMetadata.put(EntityData.VARIANT, variantColor); + } + + public void setResting(EntityMetadata entityMetadata) { + setFlag(EntityFlag.RESTING, ((BooleanEntityMetadata) entityMetadata).getPrimitiveValue()); + } + + public void setCollarColor(EntityMetadata entityMetadata) { + collarColor = (byte) ((IntEntityMetadata) entityMetadata).getPrimitiveValue(); + // Needed or else wild cats are a red color + if (getFlag(EntityFlag.TAMED)) { + dirtyMetadata.put(EntityData.COLOR, collarColor); } } diff --git a/connector/src/main/java/org/geysermc/connector/entity/living/animal/tameable/ParrotEntity.java b/connector/src/main/java/org/geysermc/connector/entity/living/animal/tameable/ParrotEntity.java index 1bb75931e..763d489e4 100644 --- a/connector/src/main/java/org/geysermc/connector/entity/living/animal/tameable/ParrotEntity.java +++ b/connector/src/main/java/org/geysermc/connector/entity/living/animal/tameable/ParrotEntity.java @@ -25,26 +25,17 @@ package org.geysermc.connector.entity.living.animal.tameable; -import com.github.steveice10.mc.protocol.data.game.entity.metadata.EntityMetadata; import com.nukkitx.math.vector.Vector3f; -import com.nukkitx.protocol.bedrock.data.entity.EntityData; -import org.geysermc.connector.entity.type.EntityType; +import org.geysermc.connector.entity.EntityDefinition; import org.geysermc.connector.network.session.GeyserSession; import org.geysermc.connector.registry.type.ItemMapping; +import java.util.UUID; + public class ParrotEntity extends TameableEntity { - public ParrotEntity(long entityId, long geyserId, EntityType entityType, Vector3f position, Vector3f motion, Vector3f rotation) { - super(entityId, geyserId, entityType, position, motion, rotation); - } - - @Override - public void updateBedrockMetadata(EntityMetadata entityMetadata, GeyserSession session) { - // Parrot color - if (entityMetadata.getId() == 19) { - metadata.put(EntityData.VARIANT, entityMetadata.getValue()); - } - super.updateBedrockMetadata(entityMetadata, session); + public ParrotEntity(GeyserSession session, long 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 diff --git a/connector/src/main/java/org/geysermc/connector/entity/living/animal/tameable/TameableEntity.java b/connector/src/main/java/org/geysermc/connector/entity/living/animal/tameable/TameableEntity.java index 923e13712..a07ce3b9b 100644 --- a/connector/src/main/java/org/geysermc/connector/entity/living/animal/tameable/TameableEntity.java +++ b/connector/src/main/java/org/geysermc/connector/entity/living/animal/tameable/TameableEntity.java @@ -26,46 +26,51 @@ package org.geysermc.connector.entity.living.animal.tameable; import com.github.steveice10.mc.protocol.data.game.entity.metadata.EntityMetadata; +import com.github.steveice10.mc.protocol.data.game.entity.metadata.type.ByteEntityMetadata; import com.nukkitx.math.vector.Vector3f; import com.nukkitx.protocol.bedrock.data.entity.EntityData; import com.nukkitx.protocol.bedrock.data.entity.EntityFlag; +import lombok.Getter; import org.geysermc.connector.entity.Entity; +import org.geysermc.connector.entity.EntityDefinition; import org.geysermc.connector.entity.living.animal.AnimalEntity; -import org.geysermc.connector.entity.type.EntityType; import org.geysermc.connector.network.session.GeyserSession; import java.util.UUID; public class TameableEntity extends AnimalEntity { + /** + * Used in the interactive tag manager to track if the session player owns this entity + */ + @Getter + protected long ownerBedrockId; - public TameableEntity(long entityId, long geyserId, EntityType entityType, Vector3f position, Vector3f motion, Vector3f rotation) { - super(entityId, geyserId, entityType, position, motion, rotation); + public TameableEntity(GeyserSession session, long 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 - public void updateBedrockMetadata(EntityMetadata entityMetadata, GeyserSession session) { - if (entityMetadata.getId() == 17) { - byte xd = (byte) entityMetadata.getValue(); - metadata.getFlags().setFlag(EntityFlag.SITTING, (xd & 0x01) == 0x01); - metadata.getFlags().setFlag(EntityFlag.ANGRY, (xd & 0x02) == 0x02); - metadata.getFlags().setFlag(EntityFlag.TAMED, (xd & 0x04) == 0x04); - } + public void setTameableFlags(EntityMetadata entityMetadata) { + byte xd = ((ByteEntityMetadata) entityMetadata).getPrimitiveValue(); + setFlag(EntityFlag.SITTING, (xd & 0x01) == 0x01); + setFlag(EntityFlag.ANGRY, (xd & 0x02) == 0x02); + setFlag(EntityFlag.TAMED, (xd & 0x04) == 0x04); + } + public void setOwner(EntityMetadata entityMetadata) { // Note: Must be set for wolf collar color to work - if (entityMetadata.getId() == 18) { - if (entityMetadata.getValue() != null) { - // Owner UUID of entity - Entity entity = session.getEntityCache().getPlayerEntity((UUID) entityMetadata.getValue()); - // Used as both a check since the player isn't in the entity cache and a normal fallback - if (entity == null) { - entity = session.getPlayerEntity(); - } - // Translate to entity ID - metadata.put(EntityData.OWNER_EID, entity.getGeyserId()); - } else { - metadata.put(EntityData.OWNER_EID, 0L); // Reset + if (entityMetadata.getValue() != null) { + // Owner UUID of entity + Entity entity = session.getEntityCache().getPlayerEntity(entityMetadata.getValue()); + // Used as both a check since the player isn't in the entity cache and a normal fallback + if (entity == null) { + entity = session.getPlayerEntity(); } + // Translate to entity ID + ownerBedrockId = entity.getGeyserId(); + } else { + // Reset + ownerBedrockId = 0L; } - super.updateBedrockMetadata(entityMetadata, session); + dirtyMetadata.put(EntityData.OWNER_EID, ownerBedrockId); } } diff --git a/connector/src/main/java/org/geysermc/connector/entity/living/animal/tameable/WolfEntity.java b/connector/src/main/java/org/geysermc/connector/entity/living/animal/tameable/WolfEntity.java index 183719dbb..5d6bcf1e3 100644 --- a/connector/src/main/java/org/geysermc/connector/entity/living/animal/tameable/WolfEntity.java +++ b/connector/src/main/java/org/geysermc/connector/entity/living/animal/tameable/WolfEntity.java @@ -26,15 +26,18 @@ package org.geysermc.connector.entity.living.animal.tameable; import com.github.steveice10.mc.protocol.data.game.entity.metadata.EntityMetadata; +import com.github.steveice10.mc.protocol.data.game.entity.metadata.type.ByteEntityMetadata; +import com.github.steveice10.mc.protocol.data.game.entity.metadata.type.IntEntityMetadata; import com.google.common.collect.ImmutableSet; import com.nukkitx.math.vector.Vector3f; import com.nukkitx.protocol.bedrock.data.entity.EntityData; import com.nukkitx.protocol.bedrock.data.entity.EntityFlag; -import org.geysermc.connector.entity.type.EntityType; +import org.geysermc.connector.entity.EntityDefinition; import org.geysermc.connector.network.session.GeyserSession; import org.geysermc.connector.registry.type.ItemMapping; import java.util.Set; +import java.util.UUID; public class WolfEntity extends TameableEntity { /** @@ -47,49 +50,45 @@ public class WolfEntity extends TameableEntity { private byte collarColor; - public WolfEntity(long entityId, long geyserId, EntityType entityType, Vector3f position, Vector3f motion, Vector3f rotation) { - super(entityId, geyserId, entityType, position, motion, rotation); + public WolfEntity(GeyserSession session, long 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 - public void updateBedrockMetadata(EntityMetadata entityMetadata, GeyserSession session) { - //Reset wolf color - if (entityMetadata.getId() == 17) { - byte xd = (byte) entityMetadata.getValue(); - boolean angry = (xd & 0x02) == 0x02; - if (angry) { - metadata.put(EntityData.COLOR, (byte) 0); - } + public void setTameableFlags(EntityMetadata entityMetadata) { + super.setFlags(entityMetadata); + // Reset wolf color + byte xd = ((ByteEntityMetadata) entityMetadata).getPrimitiveValue(); + boolean angry = (xd & 0x02) == 0x02; + if (angry) { + dirtyMetadata.put(EntityData.COLOR, (byte) 0); + } + } + + public void setCollarColor(EntityMetadata entityMetadata) { + collarColor = (byte) ((IntEntityMetadata) entityMetadata).getPrimitiveValue(); + if (getFlag(EntityFlag.ANGRY)) { + return; } - // "Begging" on wiki.vg, "Interested" in Nukkit - the tilt of the head - if (entityMetadata.getId() == 19) { - metadata.getFlags().setFlag(EntityFlag.INTERESTED, (boolean) entityMetadata.getValue()); + dirtyMetadata.put(EntityData.COLOR, collarColor); + if (ownerBedrockId == 0) { + // If a color is set and there is no owner entity ID, set one. + // Otherwise, the entire wolf is set to that color: https://user-images.githubusercontent.com/9083212/99209989-92691200-2792-11eb-911d-9a315c955be9.png + dirtyMetadata.put(EntityData.OWNER_EID, session.getPlayerEntity().getGeyserId()); } + } - // Wolf collar color - // Relies on EntityData.OWNER_EID being set in TameableEntity.java - if (entityMetadata.getId() == 20 && !metadata.getFlags().getFlag(EntityFlag.ANGRY)) { - metadata.put(EntityData.COLOR, collarColor = (byte) (int) entityMetadata.getValue()); - if (!metadata.containsKey(EntityData.OWNER_EID)) { - // If a color is set and there is no owner entity ID, set one. - // Otherwise, the entire wolf is set to that color: https://user-images.githubusercontent.com/9083212/99209989-92691200-2792-11eb-911d-9a315c955be9.png - metadata.put(EntityData.OWNER_EID, session.getPlayerEntity().getGeyserId()); - } - } - - // Wolf anger (1.16+) - if (entityMetadata.getId() == 21) { - metadata.getFlags().setFlag(EntityFlag.ANGRY, (int) entityMetadata.getValue() != 0); - metadata.put(EntityData.COLOR, (int) entityMetadata.getValue() != 0 ? (byte) 0 : collarColor); - } - - super.updateBedrockMetadata(entityMetadata, session); + // 1.16+ + public void setWolfAngerTime(EntityMetadata entityMetadata) { + int time = ((IntEntityMetadata) entityMetadata).getPrimitiveValue(); + setFlag(EntityFlag.ANGRY, time != 0); + dirtyMetadata.put(EntityData.COLOR, time != 0 ? (byte) 0 : collarColor); } @Override public boolean canEat(GeyserSession session, String javaIdentifierStripped, ItemMapping mapping) { // Cannot be a baby to eat these foods - return WOLF_FOODS.contains(javaIdentifierStripped) && !metadata.getFlags().getFlag(EntityFlag.BABY); + return WOLF_FOODS.contains(javaIdentifierStripped) && !getFlag(EntityFlag.BABY); } } diff --git a/connector/src/main/java/org/geysermc/connector/entity/living/merchant/AbstractMerchantEntity.java b/connector/src/main/java/org/geysermc/connector/entity/living/merchant/AbstractMerchantEntity.java index 84b6772b0..da7e0b357 100644 --- a/connector/src/main/java/org/geysermc/connector/entity/living/merchant/AbstractMerchantEntity.java +++ b/connector/src/main/java/org/geysermc/connector/entity/living/merchant/AbstractMerchantEntity.java @@ -26,18 +26,15 @@ package org.geysermc.connector.entity.living.merchant; import com.nukkitx.math.vector.Vector3f; +import org.geysermc.connector.entity.EntityDefinition; import org.geysermc.connector.entity.living.AgeableEntity; -import org.geysermc.connector.entity.type.EntityType; import org.geysermc.connector.network.session.GeyserSession; +import java.util.UUID; + public class AbstractMerchantEntity extends AgeableEntity { - public AbstractMerchantEntity(long entityId, long geyserId, EntityType entityType, Vector3f position, Vector3f motion, Vector3f rotation) { - super(entityId, geyserId, entityType, position, motion, rotation); - } - - @Override - public void teleport(GeyserSession session, Vector3f position, float yaw, float pitch, boolean isOnGround) { - super.teleport(session, position, yaw - 180, pitch, isOnGround); + public AbstractMerchantEntity(GeyserSession session, long 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); } } diff --git a/connector/src/main/java/org/geysermc/connector/entity/living/merchant/VillagerEntity.java b/connector/src/main/java/org/geysermc/connector/entity/living/merchant/VillagerEntity.java index f64d9f0cd..234b065d4 100644 --- a/connector/src/main/java/org/geysermc/connector/entity/living/merchant/VillagerEntity.java +++ b/connector/src/main/java/org/geysermc/connector/entity/living/merchant/VillagerEntity.java @@ -26,6 +26,7 @@ package org.geysermc.connector.entity.living.merchant; import com.github.steveice10.mc.protocol.data.game.entity.metadata.EntityMetadata; +import com.github.steveice10.mc.protocol.data.game.entity.metadata.Position; import com.github.steveice10.mc.protocol.data.game.entity.metadata.VillagerData; import com.nukkitx.math.vector.Vector3f; import com.nukkitx.math.vector.Vector3i; @@ -34,12 +35,12 @@ import com.nukkitx.protocol.bedrock.data.entity.EntityFlag; import com.nukkitx.protocol.bedrock.packet.MoveEntityAbsolutePacket; import it.unimi.dsi.fastutil.ints.Int2IntMap; import it.unimi.dsi.fastutil.ints.Int2IntOpenHashMap; -import org.geysermc.connector.entity.type.EntityType; +import lombok.Getter; +import org.geysermc.connector.entity.EntityDefinition; import org.geysermc.connector.network.session.GeyserSession; import org.geysermc.connector.registry.BlockRegistries; -import java.util.regex.Matcher; -import java.util.regex.Pattern; +import java.util.UUID; public class VillagerEntity extends AbstractMerchantEntity { @@ -79,32 +80,41 @@ public class VillagerEntity extends AbstractMerchantEntity { VILLAGER_REGIONS.put(6, 6); } - public VillagerEntity(long entityId, long geyserId, EntityType entityType, Vector3f position, Vector3f motion, Vector3f rotation) { - super(entityId, geyserId, entityType, position, motion, rotation); + private Vector3i bedPosition; + /** + * Used in the interactive tag manager + */ + @Getter + private boolean canTradeWith; + + public VillagerEntity(GeyserSession session, long 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); + } + + public void setVillagerData(EntityMetadata entityMetadata) { + VillagerData villagerData = entityMetadata.getValue(); + // Profession + int profession = VILLAGER_PROFESSIONS.get(villagerData.getProfession()); + canTradeWith = profession != 14 && profession != 0; // Not a notwit and not professionless + dirtyMetadata.put(EntityData.VARIANT, profession); + //metadata.put(EntityData.SKIN_ID, villagerData.getType()); Looks like this is modified but for any reason? + // Region + dirtyMetadata.put(EntityData.MARK_VARIANT, VILLAGER_REGIONS.get(villagerData.getType())); + // Trade tier - different indexing in Bedrock + dirtyMetadata.put(EntityData.TRADE_TIER, villagerData.getLevel() - 1); } @Override - public void updateBedrockMetadata(EntityMetadata entityMetadata, GeyserSession session) { - if (entityMetadata.getId() == 18) { - VillagerData villagerData = (VillagerData) entityMetadata.getValue(); - // Profession - metadata.put(EntityData.VARIANT, VILLAGER_PROFESSIONS.get(villagerData.getProfession())); - //metadata.put(EntityData.SKIN_ID, villagerData.getType()); Looks like this is modified but for any reason? - // Region - metadata.put(EntityData.MARK_VARIANT, VILLAGER_REGIONS.get(villagerData.getType())); - // Trade tier - different indexing in Bedrock - metadata.put(EntityData.TRADE_TIER, villagerData.getLevel() - 1); - } - super.updateBedrockMetadata(entityMetadata, session); + public Vector3i setBedPosition(EntityMetadata entityMetadata) { + return bedPosition = super.setBedPosition(entityMetadata); } @Override - public void moveRelative(GeyserSession session, double relX, double relY, double relZ, Vector3f rotation, boolean isOnGround) { + public void moveRelative(double relX, double relY, double relZ, float yaw, float pitch, float headYaw, boolean isOnGround) { // The bed block position, if it exists - Vector3i bedPosition; - if (!metadata.getFlags().getFlag(EntityFlag.SLEEPING) || (bedPosition = metadata.getPos(EntityData.BED_POSITION, null)) == null) { + if (!getFlag(EntityFlag.SLEEPING) || bedPosition == null) { // No need to worry about extra processing to compensate for sleeping - super.moveRelative(session, relX, relY, relZ, rotation, isOnGround); + super.moveRelative(relX, relY, relZ, yaw, pitch, headYaw, isOnGround); return; } @@ -133,7 +143,9 @@ public class VillagerEntity extends AbstractMerchantEntity { zOffset = .5f; } - setRotation(rotation); + setYaw(yaw); + setPitch(pitch); + setHeadYaw(headYaw); setOnGround(isOnGround); this.position = Vector3f.from(position.getX() + relX, position.getY() + relY, position.getZ() + relZ); diff --git a/connector/src/main/java/org/geysermc/connector/entity/living/monster/AbstractSkeletonEntity.java b/connector/src/main/java/org/geysermc/connector/entity/living/monster/AbstractSkeletonEntity.java index bc17e27ca..9997dd968 100644 --- a/connector/src/main/java/org/geysermc/connector/entity/living/monster/AbstractSkeletonEntity.java +++ b/connector/src/main/java/org/geysermc/connector/entity/living/monster/AbstractSkeletonEntity.java @@ -26,24 +26,25 @@ package org.geysermc.connector.entity.living.monster; import com.github.steveice10.mc.protocol.data.game.entity.metadata.EntityMetadata; +import com.github.steveice10.mc.protocol.data.game.entity.metadata.type.ByteEntityMetadata; import com.nukkitx.math.vector.Vector3f; import com.nukkitx.protocol.bedrock.data.entity.EntityData; -import org.geysermc.connector.entity.type.EntityType; +import org.geysermc.connector.entity.EntityDefinition; import org.geysermc.connector.network.session.GeyserSession; +import java.util.UUID; + public class AbstractSkeletonEntity extends MonsterEntity { - public AbstractSkeletonEntity(long entityId, long geyserId, EntityType entityType, Vector3f position, Vector3f motion, Vector3f rotation) { - super(entityId, geyserId, entityType, position, motion, rotation); + public AbstractSkeletonEntity(GeyserSession session, long 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 - public void updateBedrockMetadata(EntityMetadata entityMetadata, GeyserSession session) { - if (entityMetadata.getId() == 15) { - byte xd = (byte) entityMetadata.getValue(); - // A bit of a loophole so the hands get raised - set the target ID to its own ID - metadata.put(EntityData.TARGET_EID, ((xd & 4) == 4) ? geyserId : 0); - } - super.updateBedrockMetadata(entityMetadata, session); + public void setMobFlags(EntityMetadata entityMetadata) { + super.setMobFlags(entityMetadata); + byte xd = ((ByteEntityMetadata) entityMetadata).getPrimitiveValue(); + // A bit of a loophole so the hands get raised - set the target ID to its own ID + dirtyMetadata.put(EntityData.TARGET_EID, ((xd & 4) == 4) ? geyserId : 0); } } diff --git a/connector/src/main/java/org/geysermc/connector/entity/living/monster/BasePiglinEntity.java b/connector/src/main/java/org/geysermc/connector/entity/living/monster/BasePiglinEntity.java index cccdaf7d2..e092d3099 100644 --- a/connector/src/main/java/org/geysermc/connector/entity/living/monster/BasePiglinEntity.java +++ b/connector/src/main/java/org/geysermc/connector/entity/living/monster/BasePiglinEntity.java @@ -26,32 +26,30 @@ package org.geysermc.connector.entity.living.monster; import com.github.steveice10.mc.protocol.data.game.entity.metadata.EntityMetadata; +import com.github.steveice10.mc.protocol.data.game.entity.metadata.type.BooleanEntityMetadata; import com.nukkitx.math.vector.Vector3f; import com.nukkitx.protocol.bedrock.data.entity.EntityFlag; -import org.geysermc.connector.entity.type.EntityType; +import org.geysermc.connector.entity.EntityDefinition; import org.geysermc.connector.network.session.GeyserSession; import org.geysermc.connector.utils.DimensionUtils; +import java.util.UUID; + public class BasePiglinEntity extends MonsterEntity { private boolean isImmuneToZombification; - public BasePiglinEntity(long entityId, long geyserId, EntityType entityType, Vector3f position, Vector3f motion, Vector3f rotation) { - super(entityId, geyserId, entityType, position, motion, rotation); + public BasePiglinEntity(GeyserSession session, long 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); + } + + public void setImmuneToZombification(EntityMetadata entityMetadata) { + // Apply shaking effect if not in the nether and zombification is possible + this.isImmuneToZombification = ((BooleanEntityMetadata) entityMetadata).getPrimitiveValue(); + setFlag(EntityFlag.SHAKING, isShaking()); } @Override - public void updateBedrockMetadata(EntityMetadata entityMetadata, GeyserSession session) { - if (entityMetadata.getId() == 16) { - // Immune to zombification? - // Apply shaking effect if not in the nether and zombification is possible - this.isImmuneToZombification = (boolean) entityMetadata.getValue(); - metadata.getFlags().setFlag(EntityFlag.SHAKING, isShaking(session)); - } - super.updateBedrockMetadata(entityMetadata, session); - } - - @Override - protected boolean isShaking(GeyserSession session) { - return (!isImmuneToZombification && !session.getDimension().equals(DimensionUtils.NETHER)) || super.isShaking(session); + protected boolean isShaking() { + return (!isImmuneToZombification && !session.getDimension().equals(DimensionUtils.NETHER)) || super.isShaking(); } } diff --git a/connector/src/main/java/org/geysermc/connector/entity/living/monster/BlazeEntity.java b/connector/src/main/java/org/geysermc/connector/entity/living/monster/BlazeEntity.java index 6e1bdce53..33afdf81d 100644 --- a/connector/src/main/java/org/geysermc/connector/entity/living/monster/BlazeEntity.java +++ b/connector/src/main/java/org/geysermc/connector/entity/living/monster/BlazeEntity.java @@ -26,24 +26,22 @@ package org.geysermc.connector.entity.living.monster; import com.github.steveice10.mc.protocol.data.game.entity.metadata.EntityMetadata; +import com.github.steveice10.mc.protocol.data.game.entity.metadata.type.ByteEntityMetadata; import com.nukkitx.math.vector.Vector3f; import com.nukkitx.protocol.bedrock.data.entity.EntityFlag; -import org.geysermc.connector.entity.type.EntityType; +import org.geysermc.connector.entity.EntityDefinition; import org.geysermc.connector.network.session.GeyserSession; +import java.util.UUID; + public class BlazeEntity extends MonsterEntity { - public BlazeEntity(long entityId, long geyserId, EntityType entityType, Vector3f position, Vector3f motion, Vector3f rotation) { - super(entityId, geyserId, entityType, position, motion, rotation); + public BlazeEntity(GeyserSession session, long 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 - public void updateBedrockMetadata(EntityMetadata entityMetadata, GeyserSession session) { - if (entityMetadata.getId() == 16) { - byte xd = (byte) entityMetadata.getValue(); - metadata.getFlags().setFlag(EntityFlag.ON_FIRE, (xd & 0x01) == 0x01); - } - - super.updateBedrockMetadata(entityMetadata, session); + public void setBlazeFlags(EntityMetadata entityMetadata) { + byte xd = ((ByteEntityMetadata) entityMetadata).getPrimitiveValue(); + setFlag(EntityFlag.ON_FIRE, (xd & 0x01) == 0x01); } } diff --git a/connector/src/main/java/org/geysermc/connector/entity/living/monster/CreeperEntity.java b/connector/src/main/java/org/geysermc/connector/entity/living/monster/CreeperEntity.java index a1dc02821..0f561f8d2 100644 --- a/connector/src/main/java/org/geysermc/connector/entity/living/monster/CreeperEntity.java +++ b/connector/src/main/java/org/geysermc/connector/entity/living/monster/CreeperEntity.java @@ -26,38 +26,34 @@ package org.geysermc.connector.entity.living.monster; import com.github.steveice10.mc.protocol.data.game.entity.metadata.EntityMetadata; +import com.github.steveice10.mc.protocol.data.game.entity.metadata.type.BooleanEntityMetadata; +import com.github.steveice10.mc.protocol.data.game.entity.metadata.type.IntEntityMetadata; import com.nukkitx.math.vector.Vector3f; import com.nukkitx.protocol.bedrock.data.entity.EntityFlag; -import org.geysermc.connector.entity.type.EntityType; +import org.geysermc.connector.entity.EntityDefinition; import org.geysermc.connector.network.session.GeyserSession; -public class CreeperEntity extends MonsterEntity { +import java.util.UUID; +public class CreeperEntity extends MonsterEntity { /** - * Whether the creeper has been ignited and is using ID 17. - * In this instance we ignore ID 15 since it's sending us -1 which confuses poor Bedrock. + * Whether the creeper has been ignited and is using {@link #setIgnited(EntityMetadata)}. + * In this instance we ignore {@link #setSwelling(EntityMetadata)} since it's sending us -1 which confuses poor Bedrock. */ private boolean ignitedByFlintAndSteel = false; - public CreeperEntity(long entityId, long geyserId, EntityType entityType, Vector3f position, Vector3f motion, Vector3f rotation) { - super(entityId, geyserId, entityType, position, motion, rotation); + public CreeperEntity(GeyserSession session, long 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 - public void updateBedrockMetadata(EntityMetadata entityMetadata, GeyserSession session) { - if (entityMetadata.getId() == 16) { - if (!ignitedByFlintAndSteel) { - metadata.getFlags().setFlag(EntityFlag.IGNITED, (int) entityMetadata.getValue() == 1); - } - } - if (entityMetadata.getId() == 17) { - metadata.getFlags().setFlag(EntityFlag.POWERED, (boolean) entityMetadata.getValue()); - } - if (entityMetadata.getId() == 18) { - ignitedByFlintAndSteel = (boolean) entityMetadata.getValue(); - metadata.getFlags().setFlag(EntityFlag.IGNITED, ignitedByFlintAndSteel); + public void setSwelling(EntityMetadata entityMetadata) { + if (!ignitedByFlintAndSteel) { + setFlag(EntityFlag.IGNITED, ((IntEntityMetadata) entityMetadata).getPrimitiveValue() == 1); } + } - super.updateBedrockMetadata(entityMetadata, session); + public void setIgnited(EntityMetadata entityMetadata) { + ignitedByFlintAndSteel = ((BooleanEntityMetadata) entityMetadata).getPrimitiveValue(); + setFlag(EntityFlag.IGNITED, ignitedByFlintAndSteel); } } diff --git a/connector/src/main/java/org/geysermc/connector/entity/living/monster/ElderGuardianEntity.java b/connector/src/main/java/org/geysermc/connector/entity/living/monster/ElderGuardianEntity.java index bfbb74af9..ce98218cd 100644 --- a/connector/src/main/java/org/geysermc/connector/entity/living/monster/ElderGuardianEntity.java +++ b/connector/src/main/java/org/geysermc/connector/entity/living/monster/ElderGuardianEntity.java @@ -27,14 +27,21 @@ package org.geysermc.connector.entity.living.monster; import com.nukkitx.math.vector.Vector3f; import com.nukkitx.protocol.bedrock.data.entity.EntityFlag; -import org.geysermc.connector.entity.type.EntityType; +import org.geysermc.connector.entity.EntityDefinition; +import org.geysermc.connector.network.session.GeyserSession; + +import java.util.UUID; public class ElderGuardianEntity extends GuardianEntity { - public ElderGuardianEntity(long entityId, long geyserId, EntityType entityType, Vector3f position, Vector3f motion, Vector3f rotation) { - super(entityId, geyserId, entityType, position, motion, rotation); - // Otherwise it just looks like a normal guardian but bigger - metadata.getFlags().setFlag(EntityFlag.ELDER, true); + public ElderGuardianEntity(GeyserSession session, long 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(); + // Otherwise it just looks like a normal guardian but bigger + setFlag(EntityFlag.ELDER, true); + } } diff --git a/connector/src/main/java/org/geysermc/connector/entity/living/monster/EnderDragonEntity.java b/connector/src/main/java/org/geysermc/connector/entity/living/monster/EnderDragonEntity.java index d3d6e30d5..ff149c5b7 100644 --- a/connector/src/main/java/org/geysermc/connector/entity/living/monster/EnderDragonEntity.java +++ b/connector/src/main/java/org/geysermc/connector/entity/living/monster/EnderDragonEntity.java @@ -26,6 +26,7 @@ package org.geysermc.connector.entity.living.monster; import com.github.steveice10.mc.protocol.data.game.entity.metadata.EntityMetadata; +import com.github.steveice10.mc.protocol.data.game.entity.metadata.type.IntEntityMetadata; import com.nukkitx.math.vector.Vector3f; import com.nukkitx.protocol.bedrock.data.LevelEventType; import com.nukkitx.protocol.bedrock.data.entity.EntityData; @@ -33,17 +34,18 @@ import com.nukkitx.protocol.bedrock.data.entity.EntityEventType; import com.nukkitx.protocol.bedrock.data.entity.EntityFlag; import com.nukkitx.protocol.bedrock.packet.*; import lombok.Data; +import org.geysermc.connector.entity.EntityDefinition; import org.geysermc.connector.entity.Tickable; -import org.geysermc.connector.entity.living.InsentientEntity; -import org.geysermc.connector.entity.type.EntityType; +import org.geysermc.connector.entity.living.MobEntity; import org.geysermc.connector.network.session.GeyserSession; import org.geysermc.connector.utils.DimensionUtils; import java.util.Random; +import java.util.UUID; import java.util.concurrent.ThreadLocalRandom; import java.util.concurrent.atomic.AtomicLong; -public class EnderDragonEntity extends InsentientEntity implements Tickable { +public class EnderDragonEntity extends MobEntity implements Tickable { /** * The Ender Dragon has multiple hit boxes, which * are each its own invisible entity @@ -78,46 +80,47 @@ public class EnderDragonEntity extends InsentientEntity implements Tickable { private float wingPosition; private float lastWingPosition; - public EnderDragonEntity(long entityId, long geyserId, EntityType entityType, Vector3f position, Vector3f motion, Vector3f rotation) { - super(entityId, geyserId, entityType, position, motion, rotation); - - metadata.getFlags().setFlag(EntityFlag.FIRE_IMMUNE, true); + public EnderDragonEntity(GeyserSession session, long 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 - public void updateBedrockMetadata(EntityMetadata entityMetadata, GeyserSession session) { - if (entityMetadata.getId() == 16) { // Phase - phase = (int) entityMetadata.getValue(); - phaseTicks = 0; - metadata.getFlags().setFlag(EntityFlag.SITTING, isSitting()); - } - - super.updateBedrockMetadata(entityMetadata, session); - - 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); - } - } + protected void initializeMetadata() { + super.initializeMetadata(); + setFlag(EntityFlag.FIRE_IMMUNE, true); } @Override - public void spawnEntity(GeyserSession session) { - super.spawnEntity(session); + public void setHealth(EntityMetadata entityMetadata) { + super.setHealth(entityMetadata); + 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); + } + } + + public void setPhase(EntityMetadata entityMetadata) { + phase = ((IntEntityMetadata) entityMetadata).getPrimitiveValue(); + phaseTicks = 0; + setFlag(EntityFlag.SITTING, isSitting()); + } + + @Override + public void spawnEntity() { + super.spawnEntity(); AtomicLong nextEntityId = session.getEntityCache().getNextEntityId(); - head = new EnderDragonPartEntity(entityId + 1, nextEntityId.incrementAndGet(), EntityType.ENDER_DRAGON_PART, 1, 1); - neck = new EnderDragonPartEntity(entityId + 2, nextEntityId.incrementAndGet(), EntityType.ENDER_DRAGON_PART, 3, 3); - body = new EnderDragonPartEntity(entityId + 3, nextEntityId.incrementAndGet(), EntityType.ENDER_DRAGON_PART, 5, 3); - leftWing = new EnderDragonPartEntity(entityId + 4, nextEntityId.incrementAndGet(), EntityType.ENDER_DRAGON_PART, 4, 2); - rightWing = new EnderDragonPartEntity(entityId + 5, nextEntityId.incrementAndGet(), EntityType.ENDER_DRAGON_PART, 4, 2); + head = new EnderDragonPartEntity(session, entityId + 1, nextEntityId.incrementAndGet(), 1, 1); + neck = new EnderDragonPartEntity(session, entityId + 2, nextEntityId.incrementAndGet(), 3, 3); + body = new EnderDragonPartEntity(session, entityId + 3, nextEntityId.incrementAndGet(), 5, 3); + leftWing = new EnderDragonPartEntity(session, entityId + 4, nextEntityId.incrementAndGet(), 4, 2); + rightWing = new EnderDragonPartEntity(session, entityId + 5, nextEntityId.incrementAndGet(), 4, 2); tail = new EnderDragonPartEntity[3]; for (int i = 0; i < 3; i++) { - tail[i] = new EnderDragonPartEntity(entityId + 6 + i, nextEntityId.incrementAndGet(), EntityType.ENDER_DRAGON_PART, 2, 2); + tail[i] = new EnderDragonPartEntity(session, entityId + 6 + i, nextEntityId.incrementAndGet(), 2, 2); } allParts = new EnderDragonPartEntity[]{head, neck, body, leftWing, rightWing, tail[0], tail[1], tail[2]}; @@ -128,7 +131,7 @@ public class EnderDragonEntity extends InsentientEntity implements Tickable { for (int i = 0; i < segmentHistory.length; i++) { segmentHistory[i] = new Segment(); - segmentHistory[i].yaw = rotation.getZ(); + segmentHistory[i].yaw = headYaw; segmentHistory[i].y = position.getY(); } } @@ -141,29 +144,27 @@ public class EnderDragonEntity extends InsentientEntity implements Tickable { } @Override - public boolean despawnEntity(GeyserSession session) { + public boolean despawnEntity() { for (EnderDragonPartEntity part : allParts) { - part.despawnEntity(session); + part.despawnEntity(); } - return super.despawnEntity(session); + return super.despawnEntity(); } @Override - public void tick(GeyserSession session) { - effectTick(session); - if (!metadata.getFlags().getFlag(EntityFlag.NO_AI) && isAlive()) { + public void tick() { + effectTick(); + if (!getFlag(EntityFlag.NO_AI) && isAlive()) { pushSegment(); - updateBoundingBoxes(session); + updateBoundingBoxes(); } } /** * Updates the positions of the Ender Dragon's multiple bounding boxes - * - * @param session GeyserSession. */ - private void updateBoundingBoxes(GeyserSession session) { - Vector3f facingDir = Vector3f.createDirectionDeg(0, rotation.getZ()); + private void updateBoundingBoxes() { + Vector3f facingDir = Vector3f.createDirectionDeg(0, headYaw); Segment baseSegment = getSegment(5); // Used to angle the head, neck, and tail when the dragon flies up/down float pitch = (float) Math.toRadians(10 * (baseSegment.getY() - getSegment(10).getY())); @@ -182,7 +183,7 @@ public class EnderDragonEntity extends InsentientEntity implements Tickable { neck.setPosition(facingDir.up(pitchY).mul(pitchXZ, 1, -pitchXZ).mul(5.5f).up(headDuck)); body.setPosition(facingDir.mul(0.5f, 0f, -0.5f)); - Vector3f wingPos = Vector3f.createDirectionDeg(0, 90f - rotation.getZ()).mul(4.5f).up(2f); + Vector3f wingPos = Vector3f.createDirectionDeg(0, 90f - headYaw).mul(4.5f).up(2f); rightWing.setPosition(wingPos); leftWing.setPosition(wingPos.mul(-1, 1, -1)); // Mirror horizontally @@ -191,24 +192,23 @@ public class EnderDragonEntity extends InsentientEntity implements Tickable { float distance = (i + 1) * 2f; // Curls the tail when the dragon turns Segment targetSegment = getSegment(12 + 2 * i); - float angle = rotation.getZ() + targetSegment.yaw - baseSegment.yaw; + float angle = headYaw + targetSegment.yaw - baseSegment.yaw; float tailYOffset = targetSegment.y - baseSegment.y - (distance + 1.5f) * pitchY + 1.5f; tail[i].setPosition(Vector3f.createDirectionDeg(0, angle).mul(distance).add(tailBase).mul(-pitchXZ, 1, pitchXZ).up(tailYOffset)); } // Send updated positions for (EnderDragonPartEntity part : allParts) { - part.moveAbsolute(session, part.getPosition().add(position), Vector3f.ZERO, false, false); + part.moveAbsolute(part.getPosition().add(position), 0, 0, 0, false, false); } } /** * Handles the particles and sounds of the Ender Dragon - * @param session GeyserSession. */ - private void effectTick(GeyserSession session) { + private void effectTick() { Random random = ThreadLocalRandom.current(); - if (!metadata.getFlags().getFlag(EntityFlag.SILENT)) { + if (!getFlag(EntityFlag.SILENT)) { if (Math.cos(wingPosition * 2f * Math.PI) <= -0.3f && Math.cos(lastWingPosition * 2f * Math.PI) >= -0.3f) { PlaySoundPacket playSoundPacket = new PlaySoundPacket(); playSoundPacket.setSound("mob.enderdragon.flap"); @@ -219,14 +219,14 @@ public class EnderDragonEntity extends InsentientEntity implements Tickable { } if (!isSitting() && !isHovering() && ticksTillNextGrowl-- == 0) { - playGrowlSound(session); + playGrowlSound(); ticksTillNextGrowl = 200 + random.nextInt(200); } lastWingPosition = wingPosition; } if (isAlive()) { - if (metadata.getFlags().getFlag(EntityFlag.NO_AI)) { + if (getFlag(EntityFlag.NO_AI)) { wingPosition = 0.5f; } else if (isHovering() || isSitting()) { wingPosition += 0.1f; @@ -237,7 +237,7 @@ public class EnderDragonEntity extends InsentientEntity implements Tickable { phaseTicks++; if (phase == 3) { // Landing Phase - float headHeight = head.getMetadata().getFloat(EntityData.BOUNDING_BOX_HEIGHT); + float headHeight = head.getDirtyMetadata().getFloat(EntityData.BOUNDING_BOX_HEIGHT); //TODO Vector3f headCenter = head.getPosition().up(headHeight * 0.5f); for (int i = 0; i < 8; i++) { @@ -262,7 +262,7 @@ public class EnderDragonEntity extends InsentientEntity implements Tickable { } } } else if (phase == 7) { // Sitting Attacking Phase - playGrowlSound(session); + playGrowlSound(); } else if (phase == 9) { // Dying Phase // Send explosion particles as the dragon move towards the end portal if (phaseTicks % 10 == 0) { @@ -279,7 +279,7 @@ public class EnderDragonEntity extends InsentientEntity implements Tickable { } } - private void playGrowlSound(GeyserSession session) { + private void playGrowlSound() { Random random = ThreadLocalRandom.current(); PlaySoundPacket playSoundPacket = new PlaySoundPacket(); playSoundPacket.setSound("mob.enderdragon.growl"); @@ -306,7 +306,7 @@ public class EnderDragonEntity extends InsentientEntity implements Tickable { */ private void pushSegment() { latestSegment = (latestSegment + 1) % segmentHistory.length; - segmentHistory[latestSegment].yaw = rotation.getZ(); + segmentHistory[latestSegment].yaw = headYaw; segmentHistory[latestSegment].y = position.getY(); } diff --git a/connector/src/main/java/org/geysermc/connector/entity/living/monster/EnderDragonPartEntity.java b/connector/src/main/java/org/geysermc/connector/entity/living/monster/EnderDragonPartEntity.java index 288a3e423..ed1985057 100644 --- a/connector/src/main/java/org/geysermc/connector/entity/living/monster/EnderDragonPartEntity.java +++ b/connector/src/main/java/org/geysermc/connector/entity/living/monster/EnderDragonPartEntity.java @@ -29,15 +29,17 @@ import com.nukkitx.math.vector.Vector3f; import com.nukkitx.protocol.bedrock.data.entity.EntityData; import com.nukkitx.protocol.bedrock.data.entity.EntityFlag; import org.geysermc.connector.entity.Entity; -import org.geysermc.connector.entity.type.EntityType; +import org.geysermc.connector.entity.EntityDefinitions; +import org.geysermc.connector.network.session.GeyserSession; public class EnderDragonPartEntity extends Entity { - public EnderDragonPartEntity(long entityId, long geyserId, EntityType entityType, float width, float height) { - super(entityId, geyserId, entityType, Vector3f.ZERO, Vector3f.ZERO, Vector3f.ZERO); - metadata.put(EntityData.BOUNDING_BOX_WIDTH, width); - metadata.put(EntityData.BOUNDING_BOX_HEIGHT, height); - metadata.getFlags().setFlag(EntityFlag.INVISIBLE, true); - metadata.getFlags().setFlag(EntityFlag.FIRE_IMMUNE, true); + public EnderDragonPartEntity(GeyserSession session, long entityId, long geyserId, float width, float height) { + super(session, entityId, geyserId, null, EntityDefinitions.ENDER_DRAGON_PART, Vector3f.ZERO, Vector3f.ZERO, 0, 0, 0); + + dirtyMetadata.put(EntityData.BOUNDING_BOX_WIDTH, width); + dirtyMetadata.put(EntityData.BOUNDING_BOX_HEIGHT, height); + setFlag(EntityFlag.INVISIBLE, true); + setFlag(EntityFlag.FIRE_IMMUNE, true); } } diff --git a/connector/src/main/java/org/geysermc/connector/entity/living/monster/EndermanEntity.java b/connector/src/main/java/org/geysermc/connector/entity/living/monster/EndermanEntity.java index e187d7f0b..94265f7e4 100644 --- a/connector/src/main/java/org/geysermc/connector/entity/living/monster/EndermanEntity.java +++ b/connector/src/main/java/org/geysermc/connector/entity/living/monster/EndermanEntity.java @@ -26,41 +26,46 @@ package org.geysermc.connector.entity.living.monster; import com.github.steveice10.mc.protocol.data.game.entity.metadata.EntityMetadata; +import com.github.steveice10.mc.protocol.data.game.entity.metadata.type.BooleanEntityMetadata; +import com.github.steveice10.mc.protocol.data.game.entity.metadata.type.IntEntityMetadata; import com.nukkitx.math.vector.Vector3f; import com.nukkitx.protocol.bedrock.data.SoundEvent; import com.nukkitx.protocol.bedrock.data.entity.EntityData; import com.nukkitx.protocol.bedrock.data.entity.EntityFlag; import com.nukkitx.protocol.bedrock.packet.LevelSoundEvent2Packet; -import org.geysermc.connector.entity.type.EntityType; +import org.geysermc.connector.entity.EntityDefinition; import org.geysermc.connector.network.session.GeyserSession; +import java.util.UUID; + public class EndermanEntity extends MonsterEntity { - public EndermanEntity(long entityId, long geyserId, EntityType entityType, Vector3f position, Vector3f motion, Vector3f rotation) { - super(entityId, geyserId, entityType, position, motion, rotation); + public EndermanEntity(GeyserSession session, long 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 - public void updateBedrockMetadata(EntityMetadata entityMetadata, GeyserSession session) { - // Held block - if (entityMetadata.getId() == 16) { - metadata.put(EntityData.CARRIED_BLOCK, session.getBlockMappings().getBedrockBlockId((int) entityMetadata.getValue())); - } - // "Is screaming" - controls sound - if (entityMetadata.getId() == 17) { - if ((boolean) entityMetadata.getValue()) { - LevelSoundEvent2Packet packet = new LevelSoundEvent2Packet(); - packet.setSound(SoundEvent.STARE); - packet.setPosition(this.position); - packet.setExtraData(-1); - packet.setIdentifier("minecraft:enderman"); - session.sendUpstreamPacket(packet); - } + public void setCarriedBlock(EntityMetadata entityMetadata) { + dirtyMetadata.put(EntityData.CARRIED_BLOCK, session.getBlockMappings().getBedrockBlockId(((IntEntityMetadata) entityMetadata).getPrimitiveValue())); + } + + /** + * Controls the screaming sound + */ + public void setScreaming(EntityMetadata entityMetadata) { + //TODO see if Bedrock controls this differently + // Java Edition this controls which ambient sound is used + if (((BooleanEntityMetadata) entityMetadata).getPrimitiveValue()) { + LevelSoundEvent2Packet packet = new LevelSoundEvent2Packet(); + packet.setSound(SoundEvent.STARE); + packet.setPosition(this.position); + packet.setExtraData(-1); + packet.setIdentifier("minecraft:enderman"); + session.sendUpstreamPacket(packet); } + } + + public void setAngry(EntityMetadata entityMetadata) { // "Is staring/provoked" - controls visuals - if (entityMetadata.getId() == 18) { - metadata.getFlags().setFlag(EntityFlag.ANGRY, (boolean) entityMetadata.getValue()); - } - super.updateBedrockMetadata(entityMetadata, session); + setFlag(EntityFlag.ANGRY, ((BooleanEntityMetadata) entityMetadata).getPrimitiveValue()); } } diff --git a/connector/src/main/java/org/geysermc/connector/entity/living/monster/GhastEntity.java b/connector/src/main/java/org/geysermc/connector/entity/living/monster/GhastEntity.java index 22d91ec36..e281db30b 100644 --- a/connector/src/main/java/org/geysermc/connector/entity/living/monster/GhastEntity.java +++ b/connector/src/main/java/org/geysermc/connector/entity/living/monster/GhastEntity.java @@ -26,24 +26,23 @@ package org.geysermc.connector.entity.living.monster; import com.github.steveice10.mc.protocol.data.game.entity.metadata.EntityMetadata; +import com.github.steveice10.mc.protocol.data.game.entity.metadata.type.BooleanEntityMetadata; import com.nukkitx.math.vector.Vector3f; import com.nukkitx.protocol.bedrock.data.entity.EntityData; +import org.geysermc.connector.entity.EntityDefinition; import org.geysermc.connector.entity.living.FlyingEntity; -import org.geysermc.connector.entity.type.EntityType; import org.geysermc.connector.network.session.GeyserSession; +import java.util.UUID; + public class GhastEntity extends FlyingEntity { - public GhastEntity(long entityId, long geyserId, EntityType entityType, Vector3f position, Vector3f motion, Vector3f rotation) { - super(entityId, geyserId, entityType, position, motion, rotation); + public GhastEntity(GeyserSession session, long 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 - public void updateBedrockMetadata(EntityMetadata entityMetadata, GeyserSession session) { - if (entityMetadata.getId() == 16) { - // If the ghast is attacking - metadata.put(EntityData.CHARGE_AMOUNT, (byte) ((boolean) entityMetadata.getValue() ? 1 : 0)); - } - super.updateBedrockMetadata(entityMetadata, session); + public void setGhastAttacking(EntityMetadata entityMetadata) { + // If the ghast is attacking + dirtyMetadata.put(EntityData.CHARGE_AMOUNT, (byte) (((BooleanEntityMetadata) entityMetadata).getPrimitiveValue() ? 1 : 0)); } } diff --git a/connector/src/main/java/org/geysermc/connector/entity/living/monster/GiantEntity.java b/connector/src/main/java/org/geysermc/connector/entity/living/monster/GiantEntity.java index 65b406d54..0b1f894f1 100644 --- a/connector/src/main/java/org/geysermc/connector/entity/living/monster/GiantEntity.java +++ b/connector/src/main/java/org/geysermc/connector/entity/living/monster/GiantEntity.java @@ -27,13 +27,16 @@ package org.geysermc.connector.entity.living.monster; import com.nukkitx.math.vector.Vector3f; import com.nukkitx.protocol.bedrock.data.entity.EntityData; -import org.geysermc.connector.entity.type.EntityType; +import org.geysermc.connector.entity.EntityDefinition; +import org.geysermc.connector.network.session.GeyserSession; + +import java.util.UUID; public class GiantEntity extends MonsterEntity { - public GiantEntity(long entityId, long geyserId, EntityType entityType, Vector3f position, Vector3f motion, Vector3f rotation) { - super(entityId, geyserId, entityType, position, motion, rotation); + public GiantEntity(GeyserSession session, long 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); - metadata.put(EntityData.SCALE, 6f); + dirtyMetadata.put(EntityData.SCALE, 6f); } } diff --git a/connector/src/main/java/org/geysermc/connector/entity/living/monster/GuardianEntity.java b/connector/src/main/java/org/geysermc/connector/entity/living/monster/GuardianEntity.java index 0b28cd53e..caffb5d9e 100644 --- a/connector/src/main/java/org/geysermc/connector/entity/living/monster/GuardianEntity.java +++ b/connector/src/main/java/org/geysermc/connector/entity/living/monster/GuardianEntity.java @@ -26,33 +26,34 @@ package org.geysermc.connector.entity.living.monster; import com.github.steveice10.mc.protocol.data.game.entity.metadata.EntityMetadata; +import com.github.steveice10.mc.protocol.data.game.entity.metadata.type.IntEntityMetadata; import com.nukkitx.math.vector.Vector3f; import com.nukkitx.protocol.bedrock.data.entity.EntityData; import org.geysermc.connector.entity.Entity; -import org.geysermc.connector.entity.type.EntityType; +import org.geysermc.connector.entity.EntityDefinition; import org.geysermc.connector.network.session.GeyserSession; +import java.util.UUID; + public class GuardianEntity extends MonsterEntity { - public GuardianEntity(long entityId, long geyserId, EntityType entityType, Vector3f position, Vector3f motion, Vector3f rotation) { - super(entityId, geyserId, entityType, position, motion, rotation); + public GuardianEntity(GeyserSession session, long 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 - public void updateBedrockMetadata(EntityMetadata entityMetadata, GeyserSession session) { - if (entityMetadata.getId() == 17) { - Entity entity = session.getEntityCache().getEntityByJavaId((int) entityMetadata.getValue()); - if (entity == null && session.getPlayerEntity().getEntityId() == (Integer) entityMetadata.getValue()) { - entity = session.getPlayerEntity(); - } - - if (entity != null) { - metadata.put(EntityData.TARGET_EID, entity.getGeyserId()); - } else { - metadata.put(EntityData.TARGET_EID, (long) 0); - } + public void setGuardianTarget(EntityMetadata entityMetadata) { + int entityId = ((IntEntityMetadata) entityMetadata).getPrimitiveValue(); + Entity entity; + if (session.getPlayerEntity().getEntityId() == entityId) { + entity = session.getPlayerEntity(); + } else { + entity = session.getEntityCache().getEntityByJavaId(entityId); } - super.updateBedrockMetadata(entityMetadata, session); + if (entity != null) { + dirtyMetadata.put(EntityData.TARGET_EID, entity.getGeyserId()); + } else { + dirtyMetadata.put(EntityData.TARGET_EID, (long) 0); + } } } diff --git a/connector/src/main/java/org/geysermc/connector/entity/living/monster/MonsterEntity.java b/connector/src/main/java/org/geysermc/connector/entity/living/monster/MonsterEntity.java index 06a04d4c5..d83b72cd0 100644 --- a/connector/src/main/java/org/geysermc/connector/entity/living/monster/MonsterEntity.java +++ b/connector/src/main/java/org/geysermc/connector/entity/living/monster/MonsterEntity.java @@ -26,12 +26,15 @@ package org.geysermc.connector.entity.living.monster; import com.nukkitx.math.vector.Vector3f; +import org.geysermc.connector.entity.EntityDefinition; import org.geysermc.connector.entity.living.CreatureEntity; -import org.geysermc.connector.entity.type.EntityType; +import org.geysermc.connector.network.session.GeyserSession; + +import java.util.UUID; public class MonsterEntity extends CreatureEntity { - public MonsterEntity(long entityId, long geyserId, EntityType entityType, Vector3f position, Vector3f motion, Vector3f rotation) { - super(entityId, geyserId, entityType, position, motion, rotation); + public MonsterEntity(GeyserSession session, long 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); } } diff --git a/connector/src/main/java/org/geysermc/connector/entity/living/monster/PhantomEntity.java b/connector/src/main/java/org/geysermc/connector/entity/living/monster/PhantomEntity.java index bfa1ea724..651d1aa66 100644 --- a/connector/src/main/java/org/geysermc/connector/entity/living/monster/PhantomEntity.java +++ b/connector/src/main/java/org/geysermc/connector/entity/living/monster/PhantomEntity.java @@ -26,28 +26,27 @@ package org.geysermc.connector.entity.living.monster; import com.github.steveice10.mc.protocol.data.game.entity.metadata.EntityMetadata; +import com.github.steveice10.mc.protocol.data.game.entity.metadata.type.IntEntityMetadata; import com.nukkitx.math.vector.Vector3f; import com.nukkitx.protocol.bedrock.data.entity.EntityData; +import org.geysermc.connector.entity.EntityDefinition; import org.geysermc.connector.entity.living.FlyingEntity; -import org.geysermc.connector.entity.type.EntityType; import org.geysermc.connector.network.session.GeyserSession; +import java.util.UUID; + public class PhantomEntity extends FlyingEntity { - public PhantomEntity(long entityId, long geyserId, EntityType entityType, Vector3f position, Vector3f motion, Vector3f rotation) { - super(entityId, geyserId, entityType, position, motion, rotation); + public PhantomEntity(GeyserSession session, long 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 - public void updateBedrockMetadata(EntityMetadata entityMetadata, GeyserSession session) { - if (entityMetadata.getId() == 16) { // Size - int size = (int) entityMetadata.getValue(); - float modelScale = 1f + 0.15f * size; - float boundsScale = (1f + (0.2f * size) / EntityType.PHANTOM.getWidth()) / modelScale; + public void setPhantomScale(EntityMetadata entityMetadata) { + int size = ((IntEntityMetadata) entityMetadata).getPrimitiveValue(); + float modelScale = 1f + 0.15f * size; + float boundsScale = (1f + (0.2f * size) / definition.width()) / modelScale; - metadata.put(EntityData.BOUNDING_BOX_WIDTH, boundsScale * EntityType.PHANTOM.getWidth()); - metadata.put(EntityData.BOUNDING_BOX_HEIGHT, boundsScale * EntityType.PHANTOM.getHeight()); - metadata.put(EntityData.SCALE, modelScale); - } - super.updateBedrockMetadata(entityMetadata, session); + dirtyMetadata.put(EntityData.BOUNDING_BOX_WIDTH, boundsScale * definition.width()); + dirtyMetadata.put(EntityData.BOUNDING_BOX_HEIGHT, boundsScale * definition.height()); + dirtyMetadata.put(EntityData.SCALE, modelScale); } } diff --git a/connector/src/main/java/org/geysermc/connector/entity/living/monster/PiglinEntity.java b/connector/src/main/java/org/geysermc/connector/entity/living/monster/PiglinEntity.java index 860f6dfd3..61fb36bf4 100644 --- a/connector/src/main/java/org/geysermc/connector/entity/living/monster/PiglinEntity.java +++ b/connector/src/main/java/org/geysermc/connector/entity/living/monster/PiglinEntity.java @@ -26,44 +26,40 @@ package org.geysermc.connector.entity.living.monster; import com.github.steveice10.mc.protocol.data.game.entity.metadata.EntityMetadata; +import com.github.steveice10.mc.protocol.data.game.entity.metadata.type.BooleanEntityMetadata; import com.nukkitx.math.vector.Vector3f; import com.nukkitx.protocol.bedrock.data.entity.EntityData; import com.nukkitx.protocol.bedrock.data.entity.EntityFlag; -import org.geysermc.connector.entity.type.EntityType; +import org.geysermc.connector.entity.EntityDefinition; import org.geysermc.connector.network.session.GeyserSession; +import java.util.UUID; + public class PiglinEntity extends BasePiglinEntity { - public PiglinEntity(long entityId, long geyserId, EntityType entityType, Vector3f position, Vector3f motion, Vector3f rotation) { - super(entityId, geyserId, entityType, position, motion, rotation); + public PiglinEntity(GeyserSession session, long 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 - public void updateBedrockMetadata(EntityMetadata entityMetadata, GeyserSession session) { - if (entityMetadata.getId() == 17) { - boolean isBaby = (boolean) entityMetadata.getValue(); - if (isBaby) { - metadata.put(EntityData.SCALE, .55f); - metadata.getFlags().setFlag(EntityFlag.BABY, true); - } - } - if (entityMetadata.getId() == 18) { - metadata.getFlags().setFlag(EntityFlag.CHARGING, (boolean) entityMetadata.getValue()); - } - if (entityMetadata.getId() == 19) { - metadata.getFlags().setFlag(EntityFlag.DANCING, (boolean) entityMetadata.getValue()); - } + public void setBaby(EntityMetadata entityMetadata) { + boolean isBaby = ((BooleanEntityMetadata) entityMetadata).getPrimitiveValue(); + dirtyMetadata.put(EntityData.SCALE, isBaby? .55f : 1f); + setFlag(EntityFlag.BABY, isBaby); + } - super.updateBedrockMetadata(entityMetadata, session); + public void setChargingCrossbow(EntityMetadata entityMetadata) { + setFlag(EntityFlag.CHARGING, ((BooleanEntityMetadata) entityMetadata).getPrimitiveValue()); + } + + public void setDancing(EntityMetadata entityMetadata) { + setFlag(EntityFlag.DANCING, ((BooleanEntityMetadata) entityMetadata).getPrimitiveValue()); } @Override public void updateOffHand(GeyserSession session) { // Check if the Piglin is holding Gold and set the ADMIRING flag accordingly so its pose updates - boolean changed = metadata.getFlags().setFlag(EntityFlag.ADMIRING, session.getTagCache().shouldPiglinAdmire(session.getItemMappings().getMapping(this.offHand))); - if (changed) { - super.updateBedrockMetadata(session); - } + setFlag(EntityFlag.ADMIRING, session.getTagCache().shouldPiglinAdmire(session.getItemMappings().getMapping(this.offHand))); + super.updateBedrockMetadata(); super.updateOffHand(session); } diff --git a/connector/src/main/java/org/geysermc/connector/entity/living/monster/ShulkerEntity.java b/connector/src/main/java/org/geysermc/connector/entity/living/monster/ShulkerEntity.java index 2402e4330..e16df7211 100644 --- a/connector/src/main/java/org/geysermc/connector/entity/living/monster/ShulkerEntity.java +++ b/connector/src/main/java/org/geysermc/connector/entity/living/monster/ShulkerEntity.java @@ -26,44 +26,43 @@ package org.geysermc.connector.entity.living.monster; import com.github.steveice10.mc.protocol.data.game.entity.metadata.EntityMetadata; -import com.github.steveice10.mc.protocol.data.game.level.block.BlockFace; +import com.github.steveice10.mc.protocol.data.game.entity.metadata.type.ByteEntityMetadata; import com.nukkitx.math.vector.Vector3f; import com.nukkitx.protocol.bedrock.data.entity.EntityData; import com.nukkitx.protocol.bedrock.data.entity.EntityFlag; +import org.geysermc.connector.entity.EntityDefinition; import org.geysermc.connector.entity.living.GolemEntity; -import org.geysermc.connector.entity.type.EntityType; import org.geysermc.connector.network.session.GeyserSession; +import org.geysermc.connector.utils.Direction; + +import java.util.UUID; public class ShulkerEntity extends GolemEntity { - public ShulkerEntity(long entityId, long geyserId, EntityType entityType, Vector3f position, Vector3f motion, Vector3f rotation) { - super(entityId, geyserId, entityType, position, motion, rotation); + public ShulkerEntity(GeyserSession session, long 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); // Indicate that invisibility should be fixed through the resource pack - metadata.getFlags().setFlag(EntityFlag.BRIBED, true); + setFlag(EntityFlag.BRIBED, true); } - @Override - public void updateBedrockMetadata(EntityMetadata entityMetadata, GeyserSession session) { - if (entityMetadata.getId() == 16) { - BlockFace blockFace = (BlockFace) entityMetadata.getValue(); - metadata.put(EntityData.SHULKER_ATTACH_FACE, (byte) blockFace.ordinal()); - } + public void setAttachedFace(EntityMetadata entityMetadata) { + Direction direction = entityMetadata.getValue(); + dirtyMetadata.put(EntityData.SHULKER_ATTACH_FACE, (byte) direction.ordinal()); + } - if (entityMetadata.getId() == 17) { - int height = (byte) entityMetadata.getValue(); - metadata.put(EntityData.SHULKER_PEEK_ID, height); - } + public void setShulkerHeight(EntityMetadata entityMetadata) { + int height = ((ByteEntityMetadata) entityMetadata).getPrimitiveValue(); + dirtyMetadata.put(EntityData.SHULKER_PEEK_ID, height); + } - if (entityMetadata.getId() == 18) { - byte color = (byte) entityMetadata.getValue(); - if (color == 16) { - // 16 is default on both editions - metadata.put(EntityData.VARIANT, 16); - } else { - // Every other shulker color is offset 15 in bedrock edition - metadata.put(EntityData.VARIANT, Math.abs(color - 15)); - } + public void setShulkerColor(EntityMetadata entityMetadata) { + byte color = ((ByteEntityMetadata) entityMetadata).getPrimitiveValue(); + if (color == 16) { + // 16 is default on both editions + dirtyMetadata.put(EntityData.VARIANT, 16); + } else { + // Every other shulker color is offset 15 in bedrock edition + dirtyMetadata.put(EntityData.VARIANT, Math.abs(color - 15)); } - super.updateBedrockMetadata(entityMetadata, session); } } diff --git a/connector/src/main/java/org/geysermc/connector/entity/living/monster/SkeletonEntity.java b/connector/src/main/java/org/geysermc/connector/entity/living/monster/SkeletonEntity.java index 44c1d1f85..984590db1 100644 --- a/connector/src/main/java/org/geysermc/connector/entity/living/monster/SkeletonEntity.java +++ b/connector/src/main/java/org/geysermc/connector/entity/living/monster/SkeletonEntity.java @@ -26,29 +26,28 @@ package org.geysermc.connector.entity.living.monster; import com.github.steveice10.mc.protocol.data.game.entity.metadata.EntityMetadata; +import com.github.steveice10.mc.protocol.data.game.entity.metadata.type.BooleanEntityMetadata; import com.nukkitx.math.vector.Vector3f; import com.nukkitx.protocol.bedrock.data.entity.EntityFlag; -import org.geysermc.connector.entity.type.EntityType; +import org.geysermc.connector.entity.EntityDefinition; import org.geysermc.connector.network.session.GeyserSession; +import java.util.UUID; + public class SkeletonEntity extends AbstractSkeletonEntity { private boolean convertingToStray = false; - public SkeletonEntity(long entityId, long geyserId, EntityType entityType, Vector3f position, Vector3f motion, Vector3f rotation) { - super(entityId, geyserId, entityType, position, motion, rotation); + public SkeletonEntity(GeyserSession session, long 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); + } + + public void setConvertingToStray(EntityMetadata entityMetadata) { + this.convertingToStray = ((BooleanEntityMetadata) entityMetadata).getPrimitiveValue(); + setFlag(EntityFlag.SHAKING, isShaking()); } @Override - public void updateBedrockMetadata(EntityMetadata entityMetadata, GeyserSession session) { - super.updateBedrockMetadata(entityMetadata, session); - if (entityMetadata.getId() == 16) { - this.convertingToStray = (boolean) entityMetadata.getValue(); - metadata.getFlags().setFlag(EntityFlag.SHAKING, isShaking(session)); - } - } - - @Override - protected boolean isShaking(GeyserSession session) { + protected boolean isShaking() { return convertingToStray; } } diff --git a/connector/src/main/java/org/geysermc/connector/entity/living/monster/SpiderEntity.java b/connector/src/main/java/org/geysermc/connector/entity/living/monster/SpiderEntity.java index 65c30289a..f2f151eae 100644 --- a/connector/src/main/java/org/geysermc/connector/entity/living/monster/SpiderEntity.java +++ b/connector/src/main/java/org/geysermc/connector/entity/living/monster/SpiderEntity.java @@ -26,24 +26,22 @@ package org.geysermc.connector.entity.living.monster; import com.github.steveice10.mc.protocol.data.game.entity.metadata.EntityMetadata; +import com.github.steveice10.mc.protocol.data.game.entity.metadata.type.ByteEntityMetadata; import com.nukkitx.math.vector.Vector3f; import com.nukkitx.protocol.bedrock.data.entity.EntityFlag; -import org.geysermc.connector.entity.type.EntityType; +import org.geysermc.connector.entity.EntityDefinition; import org.geysermc.connector.network.session.GeyserSession; +import java.util.UUID; + public class SpiderEntity extends MonsterEntity { - public SpiderEntity(long entityId, long geyserId, EntityType entityType, Vector3f position, Vector3f motion, Vector3f rotation) { - super(entityId, geyserId, entityType, position, motion, rotation); + public SpiderEntity(GeyserSession session, long 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 - public void updateBedrockMetadata(EntityMetadata entityMetadata, GeyserSession session) { - if (entityMetadata.getId() == 16) { - byte xd = (byte) entityMetadata.getValue(); - metadata.getFlags().setFlag(EntityFlag.WALL_CLIMBING, (xd & 0x01) == 0x01); - } - - super.updateBedrockMetadata(entityMetadata, session); + public void setSpiderFlags(EntityMetadata entityMetadata) { + byte xd = ((ByteEntityMetadata) entityMetadata).getPrimitiveValue(); + setFlag(EntityFlag.WALL_CLIMBING, (xd & 0x01) == 0x01); } } diff --git a/connector/src/main/java/org/geysermc/connector/entity/living/monster/VexEntity.java b/connector/src/main/java/org/geysermc/connector/entity/living/monster/VexEntity.java index 990a2f3a9..d6e05b387 100644 --- a/connector/src/main/java/org/geysermc/connector/entity/living/monster/VexEntity.java +++ b/connector/src/main/java/org/geysermc/connector/entity/living/monster/VexEntity.java @@ -26,25 +26,24 @@ package org.geysermc.connector.entity.living.monster; import com.github.steveice10.mc.protocol.data.game.entity.metadata.EntityMetadata; +import com.github.steveice10.mc.protocol.data.game.entity.metadata.type.ByteEntityMetadata; import com.nukkitx.math.vector.Vector3f; import com.nukkitx.protocol.bedrock.data.entity.EntityData; -import org.geysermc.connector.entity.type.EntityType; +import org.geysermc.connector.entity.EntityDefinition; import org.geysermc.connector.network.session.GeyserSession; +import java.util.UUID; + public class VexEntity extends MonsterEntity { - public VexEntity(long entityId, long geyserId, EntityType entityType, Vector3f position, Vector3f motion, Vector3f rotation) { - super(entityId, geyserId, entityType, position, motion, rotation); + public VexEntity(GeyserSession session, long 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 - public void updateBedrockMetadata(EntityMetadata entityMetadata, GeyserSession session) { - if (entityMetadata.getId() == 16) { - byte xd = (byte) entityMetadata.getValue(); - // Set the target to the player to force the attack animation - // even if the player isn't the target as we dont get the target on Java - metadata.put(EntityData.TARGET_EID, (xd & 0x01) == 0x01 ? session.getPlayerEntity().getGeyserId() : 0); - } - super.updateBedrockMetadata(entityMetadata, session); + public void setVexFlags(EntityMetadata entityMetadata) { + byte xd = ((ByteEntityMetadata) entityMetadata).getPrimitiveValue(); + // Set the target to the player to force the attack animation + // even if the player isn't the target as we dont get the target on Java + dirtyMetadata.put(EntityData.TARGET_EID, (xd & 0x01) == 0x01 ? session.getPlayerEntity().getGeyserId() : 0); } } diff --git a/connector/src/main/java/org/geysermc/connector/entity/living/monster/WitherEntity.java b/connector/src/main/java/org/geysermc/connector/entity/living/monster/WitherEntity.java index d6d7f8074..befb464fe 100644 --- a/connector/src/main/java/org/geysermc/connector/entity/living/monster/WitherEntity.java +++ b/connector/src/main/java/org/geysermc/connector/entity/living/monster/WitherEntity.java @@ -26,52 +26,62 @@ package org.geysermc.connector.entity.living.monster; import com.github.steveice10.mc.protocol.data.game.entity.metadata.EntityMetadata; +import com.github.steveice10.mc.protocol.data.game.entity.metadata.type.IntEntityMetadata; import com.nukkitx.math.vector.Vector3f; import com.nukkitx.protocol.bedrock.data.entity.EntityData; import org.geysermc.connector.entity.Entity; -import org.geysermc.connector.entity.type.EntityType; +import org.geysermc.connector.entity.EntityDefinition; import org.geysermc.connector.network.session.GeyserSession; +import java.util.UUID; + public class WitherEntity extends MonsterEntity { - public WitherEntity(long entityId, long geyserId, EntityType entityType, Vector3f position, Vector3f motion, Vector3f rotation) { - super(entityId, geyserId, entityType, position, motion, rotation); - - metadata.put(EntityData.WITHER_AERIAL_ATTACK, (short) 1); + public WitherEntity(GeyserSession session, long 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 - public void updateBedrockMetadata(EntityMetadata entityMetadata, GeyserSession session) { - long targetID = 0; + protected void initializeMetadata() { + super.initializeMetadata(); + dirtyMetadata.put(EntityData.WITHER_AERIAL_ATTACK, (short) 1); + } - if (entityMetadata.getId() >= 16 && entityMetadata.getId() <= 18) { - Entity entity = session.getEntityCache().getEntityByJavaId((int) entityMetadata.getValue()); - if (entity == null && session.getPlayerEntity().getEntityId() == (int) entityMetadata.getValue()) { - entity = session.getPlayerEntity(); - } + public void setTarget1(EntityMetadata entityMetadata) { + setTargetId(EntityData.WITHER_TARGET_1, entityMetadata); + } - if (entity != null) { - targetID = entity.getGeyserId(); - } + public void setTarget2(EntityMetadata entityMetadata) { + setTargetId(EntityData.WITHER_TARGET_2, entityMetadata); + } + + public void setTarget3(EntityMetadata entityMetadata) { + setTargetId(EntityData.WITHER_TARGET_3, entityMetadata); + } + + private void setTargetId(EntityData entityData, EntityMetadata entityMetadata) { + int entityId = ((IntEntityMetadata) entityMetadata).getPrimitiveValue(); + Entity entity; + if (session.getPlayerEntity().getEntityId() == entityId) { + entity = session.getPlayerEntity(); + } else { + entity = session.getEntityCache().getEntityByJavaId(entityId); } - if (entityMetadata.getId() == 16) { - metadata.put(EntityData.WITHER_TARGET_1, targetID); - } else if (entityMetadata.getId() == 17) { - metadata.put(EntityData.WITHER_TARGET_2, targetID); - } else if (entityMetadata.getId() == 18) { - metadata.put(EntityData.WITHER_TARGET_3, targetID); - } else if (entityMetadata.getId() == 19) { - metadata.put(EntityData.WITHER_INVULNERABLE_TICKS, entityMetadata.getValue()); - - // Show the shield for the first few seconds of spawning (like Java) - if ((int) entityMetadata.getValue() >= 165) { - metadata.put(EntityData.WITHER_AERIAL_ATTACK, (short) 0); - } else { - metadata.put(EntityData.WITHER_AERIAL_ATTACK, (short) 1); - } + if (entity != null) { + dirtyMetadata.put(entityData, entity.getGeyserId()); } + } - super.updateBedrockMetadata(entityMetadata, session); + public void setInvulnerableTicks(EntityMetadata entityMetadata) { + int value = ((IntEntityMetadata) entityMetadata).getPrimitiveValue(); + dirtyMetadata.put(EntityData.WITHER_INVULNERABLE_TICKS, value); + + // Show the shield for the first few seconds of spawning (like Java) + if (value >= 165) { + dirtyMetadata.put(EntityData.WITHER_AERIAL_ATTACK, (short) 0); + } else { + dirtyMetadata.put(EntityData.WITHER_AERIAL_ATTACK, (short) 1); + } } } diff --git a/connector/src/main/java/org/geysermc/connector/entity/living/monster/ZoglinEntity.java b/connector/src/main/java/org/geysermc/connector/entity/living/monster/ZoglinEntity.java index dde19927d..4773b2d80 100644 --- a/connector/src/main/java/org/geysermc/connector/entity/living/monster/ZoglinEntity.java +++ b/connector/src/main/java/org/geysermc/connector/entity/living/monster/ZoglinEntity.java @@ -26,27 +26,24 @@ package org.geysermc.connector.entity.living.monster; import com.github.steveice10.mc.protocol.data.game.entity.metadata.EntityMetadata; +import com.github.steveice10.mc.protocol.data.game.entity.metadata.type.BooleanEntityMetadata; import com.nukkitx.math.vector.Vector3f; import com.nukkitx.protocol.bedrock.data.entity.EntityData; import com.nukkitx.protocol.bedrock.data.entity.EntityFlag; -import org.geysermc.connector.entity.type.EntityType; +import org.geysermc.connector.entity.EntityDefinition; import org.geysermc.connector.network.session.GeyserSession; +import java.util.UUID; + public class ZoglinEntity extends MonsterEntity { - public ZoglinEntity(long entityId, long geyserId, EntityType entityType, Vector3f position, Vector3f motion, Vector3f rotation) { - super(entityId, geyserId, entityType, position, motion, rotation); + public ZoglinEntity(GeyserSession session, long 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 - public void updateBedrockMetadata(EntityMetadata entityMetadata, GeyserSession session) { - if (entityMetadata.getId() == 16) { - boolean isBaby = (boolean) entityMetadata.getValue(); - if (isBaby) { - metadata.put(EntityData.SCALE, .55f); - metadata.getFlags().setFlag(EntityFlag.BABY, true); - } - } - super.updateBedrockMetadata(entityMetadata, session); + public void setBaby(EntityMetadata entityMetadata) { + boolean isBaby = ((BooleanEntityMetadata) entityMetadata).getPrimitiveValue(); + dirtyMetadata.put(EntityData.SCALE, isBaby? .55f : 1f); + setFlag(EntityFlag.BABY, isBaby); } } diff --git a/connector/src/main/java/org/geysermc/connector/entity/living/monster/ZombieEntity.java b/connector/src/main/java/org/geysermc/connector/entity/living/monster/ZombieEntity.java index bae593e4f..fd9218cd7 100644 --- a/connector/src/main/java/org/geysermc/connector/entity/living/monster/ZombieEntity.java +++ b/connector/src/main/java/org/geysermc/connector/entity/living/monster/ZombieEntity.java @@ -26,34 +26,35 @@ package org.geysermc.connector.entity.living.monster; import com.github.steveice10.mc.protocol.data.game.entity.metadata.EntityMetadata; +import com.github.steveice10.mc.protocol.data.game.entity.metadata.type.BooleanEntityMetadata; import com.nukkitx.math.vector.Vector3f; import com.nukkitx.protocol.bedrock.data.entity.EntityData; import com.nukkitx.protocol.bedrock.data.entity.EntityFlag; -import org.geysermc.connector.entity.type.EntityType; +import org.geysermc.connector.entity.EntityDefinition; import org.geysermc.connector.network.session.GeyserSession; +import java.util.UUID; + public class ZombieEntity extends MonsterEntity { private boolean convertingToDrowned = false; - public ZombieEntity(long entityId, long geyserId, EntityType entityType, Vector3f position, Vector3f motion, Vector3f rotation) { - super(entityId, geyserId, entityType, position, motion, rotation); + public ZombieEntity(GeyserSession session, long 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); + } + + public void setZombieBaby(EntityMetadata entityMetadata) { + boolean isBaby = ((BooleanEntityMetadata) entityMetadata).getPrimitiveValue(); + dirtyMetadata.put(EntityData.SCALE, isBaby ? .55f : 1.0f); + setFlag(EntityFlag.BABY, isBaby); + } + + public void setConvertingToDrowned(EntityMetadata entityMetadata) { + convertingToDrowned = ((BooleanEntityMetadata) entityMetadata).getPrimitiveValue(); + setFlag(EntityFlag.SHAKING, isShaking()); } @Override - public void updateBedrockMetadata(EntityMetadata entityMetadata, GeyserSession session) { - if (entityMetadata.getId() == 16) { - boolean isBaby = (boolean) entityMetadata.getValue(); - metadata.put(EntityData.SCALE, isBaby ? .55f : 1.0f); - metadata.getFlags().setFlag(EntityFlag.BABY, isBaby); - } else if (entityMetadata.getId() == 18) { - convertingToDrowned = (boolean) entityMetadata.getValue(); - metadata.getFlags().setFlag(EntityFlag.SHAKING, isShaking(session)); - } - super.updateBedrockMetadata(entityMetadata, session); - } - - @Override - protected boolean isShaking(GeyserSession session) { - return convertingToDrowned || super.isShaking(session); + protected boolean isShaking() { + return convertingToDrowned || super.isShaking(); } } diff --git a/connector/src/main/java/org/geysermc/connector/entity/living/monster/ZombieVillagerEntity.java b/connector/src/main/java/org/geysermc/connector/entity/living/monster/ZombieVillagerEntity.java index 2e3308ec1..744d3e559 100644 --- a/connector/src/main/java/org/geysermc/connector/entity/living/monster/ZombieVillagerEntity.java +++ b/connector/src/main/java/org/geysermc/connector/entity/living/monster/ZombieVillagerEntity.java @@ -27,39 +27,39 @@ package org.geysermc.connector.entity.living.monster; import com.github.steveice10.mc.protocol.data.game.entity.metadata.EntityMetadata; import com.github.steveice10.mc.protocol.data.game.entity.metadata.VillagerData; +import com.github.steveice10.mc.protocol.data.game.entity.metadata.type.BooleanEntityMetadata; import com.nukkitx.math.vector.Vector3f; import com.nukkitx.protocol.bedrock.data.entity.EntityData; import com.nukkitx.protocol.bedrock.data.entity.EntityFlag; +import org.geysermc.connector.entity.EntityDefinition; import org.geysermc.connector.entity.living.merchant.VillagerEntity; -import org.geysermc.connector.entity.type.EntityType; import org.geysermc.connector.network.session.GeyserSession; +import java.util.UUID; + public class ZombieVillagerEntity extends ZombieEntity { private boolean isTransforming; - public ZombieVillagerEntity(long entityId, long geyserId, EntityType entityType, Vector3f position, Vector3f motion, Vector3f rotation) { - super(entityId, geyserId, entityType, position, motion, rotation); + public ZombieVillagerEntity(GeyserSession session, long 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); + } + + public void setTransforming(EntityMetadata entityMetadata) { + isTransforming = ((BooleanEntityMetadata) entityMetadata).getPrimitiveValue(); + setFlag(EntityFlag.IS_TRANSFORMING, isTransforming); + setFlag(EntityFlag.SHAKING, isShaking()); + } + + public void setZombieVillagerData(EntityMetadata entityMetadata) { + VillagerData villagerData = entityMetadata.getValue(); + dirtyMetadata.put(EntityData.VARIANT, VillagerEntity.VILLAGER_PROFESSIONS.get(villagerData.getProfession())); // Actually works properly with the OptionalPack + dirtyMetadata.put(EntityData.MARK_VARIANT, VillagerEntity.VILLAGER_REGIONS.get(villagerData.getType())); + // Used with the OptionalPack + dirtyMetadata.put(EntityData.TRADE_TIER, villagerData.getLevel() - 1); } @Override - public void updateBedrockMetadata(EntityMetadata entityMetadata, GeyserSession session) { - if (entityMetadata.getId() == 19) { - isTransforming = (boolean) entityMetadata.getValue(); - metadata.getFlags().setFlag(EntityFlag.IS_TRANSFORMING, (boolean) entityMetadata.getValue()); - metadata.getFlags().setFlag(EntityFlag.SHAKING, isShaking(session)); - } - if (entityMetadata.getId() == 20) { - VillagerData villagerData = (VillagerData) entityMetadata.getValue(); - metadata.put(EntityData.VARIANT, VillagerEntity.VILLAGER_PROFESSIONS.get(villagerData.getProfession())); // Actually works properly with the OptionalPack - metadata.put(EntityData.MARK_VARIANT, VillagerEntity.VILLAGER_REGIONS.get(villagerData.getType())); - // Used with the OptionalPack - metadata.put(EntityData.TRADE_TIER, villagerData.getLevel() - 1); - } - super.updateBedrockMetadata(entityMetadata, session); - } - - @Override - protected boolean isShaking(GeyserSession session) { - return isTransforming || super.isShaking(session); + protected boolean isShaking() { + return isTransforming || super.isShaking(); } } diff --git a/connector/src/main/java/org/geysermc/connector/entity/living/monster/ZombifiedPiglinEntity.java b/connector/src/main/java/org/geysermc/connector/entity/living/monster/ZombifiedPiglinEntity.java index ad00145bc..ddfd3184b 100644 --- a/connector/src/main/java/org/geysermc/connector/entity/living/monster/ZombifiedPiglinEntity.java +++ b/connector/src/main/java/org/geysermc/connector/entity/living/monster/ZombifiedPiglinEntity.java @@ -27,13 +27,16 @@ package org.geysermc.connector.entity.living.monster; import com.nukkitx.math.vector.Vector3f; import com.nukkitx.protocol.bedrock.data.entity.EntityFlag; -import org.geysermc.connector.entity.type.EntityType; +import org.geysermc.connector.entity.EntityDefinition; +import org.geysermc.connector.network.session.GeyserSession; + +import java.util.UUID; public class ZombifiedPiglinEntity extends ZombieEntity { - public ZombifiedPiglinEntity(long entityId, long geyserId, EntityType entityType, Vector3f position, Vector3f motion, Vector3f rotation) { - super(entityId, geyserId, entityType, position, motion, rotation); + public ZombifiedPiglinEntity(GeyserSession session, long 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); - metadata.getFlags().setFlag(EntityFlag.FIRE_IMMUNE, true); + setFlag(EntityFlag.FIRE_IMMUNE, true); } } diff --git a/connector/src/main/java/org/geysermc/connector/entity/living/monster/raid/AbstractIllagerEntity.java b/connector/src/main/java/org/geysermc/connector/entity/living/monster/raid/AbstractIllagerEntity.java index e0fa18001..8350ae775 100644 --- a/connector/src/main/java/org/geysermc/connector/entity/living/monster/raid/AbstractIllagerEntity.java +++ b/connector/src/main/java/org/geysermc/connector/entity/living/monster/raid/AbstractIllagerEntity.java @@ -26,11 +26,14 @@ package org.geysermc.connector.entity.living.monster.raid; import com.nukkitx.math.vector.Vector3f; -import org.geysermc.connector.entity.type.EntityType; +import org.geysermc.connector.entity.EntityDefinition; +import org.geysermc.connector.network.session.GeyserSession; + +import java.util.UUID; public class AbstractIllagerEntity extends RaidParticipantEntity { - public AbstractIllagerEntity(long entityId, long geyserId, EntityType entityType, Vector3f position, Vector3f motion, Vector3f rotation) { - super(entityId, geyserId, entityType, position, motion, rotation); + public AbstractIllagerEntity(GeyserSession session, long 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); } } diff --git a/connector/src/main/java/org/geysermc/connector/entity/living/monster/raid/PillagerEntity.java b/connector/src/main/java/org/geysermc/connector/entity/living/monster/raid/PillagerEntity.java index 325cac7ab..d2cb661c6 100644 --- a/connector/src/main/java/org/geysermc/connector/entity/living/monster/raid/PillagerEntity.java +++ b/connector/src/main/java/org/geysermc/connector/entity/living/monster/raid/PillagerEntity.java @@ -27,26 +27,28 @@ package org.geysermc.connector.entity.living.monster.raid; import com.nukkitx.math.vector.Vector3f; import com.nukkitx.protocol.bedrock.data.entity.EntityFlag; -import org.geysermc.connector.entity.type.EntityType; +import org.geysermc.connector.entity.EntityDefinition; import org.geysermc.connector.network.session.GeyserSession; import org.geysermc.connector.registry.type.ItemMapping; +import java.util.UUID; + public class PillagerEntity extends AbstractIllagerEntity { - public PillagerEntity(long entityId, long geyserId, EntityType entityType, Vector3f position, Vector3f motion, Vector3f rotation) { - super(entityId, geyserId, entityType, position, motion, rotation); + public PillagerEntity(GeyserSession session, long 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 - public void updateMainHand(GeyserSession session) { - checkForCrossbow(session); + public void updateMainHand(GeyserSession session) { //TODO + checkForCrossbow(); super.updateMainHand(session); } @Override public void updateOffHand(GeyserSession session) { - checkForCrossbow(session); + checkForCrossbow(); super.updateOffHand(session); } @@ -54,15 +56,13 @@ public class PillagerEntity extends AbstractIllagerEntity { /** * Check for a crossbow in either the mainhand or offhand. If one exists, indicate that the pillager should be posing */ - protected void checkForCrossbow(GeyserSession session) { + protected void checkForCrossbow() { ItemMapping crossbow = session.getItemMappings().getStoredItems().crossbow(); boolean hasCrossbow = this.hand.getId() == crossbow.getBedrockId() || this.offHand.getId() == crossbow.getBedrockId(); - boolean usingItemChanged = metadata.getFlags().setFlag(EntityFlag.USING_ITEM, hasCrossbow); - boolean chargedChanged = metadata.getFlags().setFlag(EntityFlag.CHARGED, hasCrossbow); + setFlag(EntityFlag.USING_ITEM, hasCrossbow); + setFlag(EntityFlag.CHARGED, hasCrossbow); - if (usingItemChanged || chargedChanged) { - updateBedrockMetadata(session); - } + updateBedrockMetadata(); } } diff --git a/connector/src/main/java/org/geysermc/connector/entity/living/monster/raid/RaidParticipantEntity.java b/connector/src/main/java/org/geysermc/connector/entity/living/monster/raid/RaidParticipantEntity.java index 15248f454..fcfd50425 100644 --- a/connector/src/main/java/org/geysermc/connector/entity/living/monster/raid/RaidParticipantEntity.java +++ b/connector/src/main/java/org/geysermc/connector/entity/living/monster/raid/RaidParticipantEntity.java @@ -26,12 +26,15 @@ package org.geysermc.connector.entity.living.monster.raid; import com.nukkitx.math.vector.Vector3f; +import org.geysermc.connector.entity.EntityDefinition; import org.geysermc.connector.entity.living.monster.MonsterEntity; -import org.geysermc.connector.entity.type.EntityType; +import org.geysermc.connector.network.session.GeyserSession; + +import java.util.UUID; public class RaidParticipantEntity extends MonsterEntity { - public RaidParticipantEntity(long entityId, long geyserId, EntityType entityType, Vector3f position, Vector3f motion, Vector3f rotation) { - super(entityId, geyserId, entityType, position, motion, rotation); + public RaidParticipantEntity(GeyserSession session, long 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); } } diff --git a/connector/src/main/java/org/geysermc/connector/entity/living/monster/raid/SpellcasterIllagerEntity.java b/connector/src/main/java/org/geysermc/connector/entity/living/monster/raid/SpellcasterIllagerEntity.java index 693220dda..23485750d 100644 --- a/connector/src/main/java/org/geysermc/connector/entity/living/monster/raid/SpellcasterIllagerEntity.java +++ b/connector/src/main/java/org/geysermc/connector/entity/living/monster/raid/SpellcasterIllagerEntity.java @@ -26,38 +26,38 @@ package org.geysermc.connector.entity.living.monster.raid; import com.github.steveice10.mc.protocol.data.game.entity.metadata.EntityMetadata; +import com.github.steveice10.mc.protocol.data.game.entity.metadata.type.ByteEntityMetadata; import com.nukkitx.math.vector.Vector3f; import com.nukkitx.protocol.bedrock.data.entity.EntityData; import com.nukkitx.protocol.bedrock.data.entity.EntityFlag; -import org.geysermc.connector.entity.type.EntityType; +import org.geysermc.connector.entity.EntityDefinition; +import org.geysermc.connector.entity.EntityDefinitions; import org.geysermc.connector.network.session.GeyserSession; +import java.util.UUID; + public class SpellcasterIllagerEntity extends AbstractIllagerEntity { private static final int SUMMON_VEX_PARTICLE_COLOR = (179 << 16) | (179 << 8) | 204; private static final int ATTACK_PARTICLE_COLOR = (102 << 16) | (77 << 8) | 89; private static final int WOLOLO_PARTICLE_COLOR = (179 << 16) | (128 << 8) | 51; - public SpellcasterIllagerEntity(long entityId, long geyserId, EntityType entityType, Vector3f position, Vector3f motion, Vector3f rotation) { - super(entityId, geyserId, entityType, position, motion, rotation); + public SpellcasterIllagerEntity(GeyserSession session, long 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); // OptionalPack usage - metadata.getFlags().setFlag(EntityFlag.BRIBED, this.entityType == EntityType.ILLUSIONER); + dirtyMetadata.getFlags().setFlag(EntityFlag.BRIBED, this.definition == EntityDefinitions.ILLUSIONER); } - @Override - public void updateBedrockMetadata(EntityMetadata entityMetadata, GeyserSession session) { - if (entityMetadata.getId() == 17) { - int spellType = (int) (byte) entityMetadata.getValue(); - // Summon vex, attack, or wololo - metadata.getFlags().setFlag(EntityFlag.CASTING, spellType == 1 || spellType == 2 || spellType == 3); - int rgbData = switch (spellType) { - // Set the spell color based on Java values - case 1 -> SUMMON_VEX_PARTICLE_COLOR; - case 2 -> ATTACK_PARTICLE_COLOR; - case 3 -> WOLOLO_PARTICLE_COLOR; - default -> 0; - }; - metadata.put(EntityData.EVOKER_SPELL_COLOR, rgbData); - } - super.updateBedrockMetadata(entityMetadata, session); + public void setSpellType(EntityMetadata entityMetadata) { + int spellType = ((ByteEntityMetadata) entityMetadata).getPrimitiveValue(); + // Summon vex, attack, or wololo + setFlag(EntityFlag.CASTING, spellType == 1 || spellType == 2 || spellType == 3); + int rgbData = switch (spellType) { + // Set the spell color based on Java values + case 1 -> SUMMON_VEX_PARTICLE_COLOR; + case 2 -> ATTACK_PARTICLE_COLOR; + case 3 -> WOLOLO_PARTICLE_COLOR; + default -> 0; + }; + dirtyMetadata.put(EntityData.EVOKER_SPELL_COLOR, rgbData); } } diff --git a/connector/src/main/java/org/geysermc/connector/entity/living/monster/raid/VindicatorEntity.java b/connector/src/main/java/org/geysermc/connector/entity/living/monster/raid/VindicatorEntity.java index 4a9360393..74616806c 100644 --- a/connector/src/main/java/org/geysermc/connector/entity/living/monster/raid/VindicatorEntity.java +++ b/connector/src/main/java/org/geysermc/connector/entity/living/monster/raid/VindicatorEntity.java @@ -26,24 +26,25 @@ package org.geysermc.connector.entity.living.monster.raid; import com.github.steveice10.mc.protocol.data.game.entity.metadata.EntityMetadata; +import com.github.steveice10.mc.protocol.data.game.entity.metadata.type.ByteEntityMetadata; import com.nukkitx.math.vector.Vector3f; import com.nukkitx.protocol.bedrock.data.entity.EntityFlag; -import org.geysermc.connector.entity.type.EntityType; +import org.geysermc.connector.entity.EntityDefinition; import org.geysermc.connector.network.session.GeyserSession; +import java.util.UUID; + public class VindicatorEntity extends AbstractIllagerEntity { - public VindicatorEntity(long entityId, long geyserId, EntityType entityType, Vector3f position, Vector3f motion, Vector3f rotation) { - super(entityId, geyserId, entityType, position, motion, rotation); + public VindicatorEntity(GeyserSession session, long 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 - public void updateBedrockMetadata(EntityMetadata entityMetadata, GeyserSession session) { + public void setMobFlags(EntityMetadata entityMetadata) { + super.setMobFlags(entityMetadata); // Allow the axe to be shown if necessary - if (entityMetadata.getId() == 15) { - byte xd = (byte) entityMetadata.getValue(); - metadata.getFlags().setFlag(EntityFlag.ANGRY, (xd & 4) == 4); - } - super.updateBedrockMetadata(entityMetadata, session); + byte xd = ((ByteEntityMetadata) entityMetadata).getPrimitiveValue(); + setFlag(EntityFlag.ANGRY, (xd & 4) == 4); } } diff --git a/connector/src/main/java/org/geysermc/connector/entity/player/PlayerEntity.java b/connector/src/main/java/org/geysermc/connector/entity/player/PlayerEntity.java index 5b948ef37..cfa78e08a 100644 --- a/connector/src/main/java/org/geysermc/connector/entity/player/PlayerEntity.java +++ b/connector/src/main/java/org/geysermc/connector/entity/player/PlayerEntity.java @@ -28,6 +28,8 @@ package org.geysermc.connector.entity.player; import com.github.steveice10.mc.auth.data.GameProfile; 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.type.ByteEntityMetadata; +import com.github.steveice10.mc.protocol.data.game.entity.metadata.type.FloatEntityMetadata; import com.github.steveice10.mc.protocol.data.game.scoreboard.ScoreboardPosition; import com.github.steveice10.mc.protocol.data.game.scoreboard.TeamColor; import com.github.steveice10.opennbt.tag.builtin.CompoundTag; @@ -45,9 +47,9 @@ import lombok.Setter; import net.kyori.adventure.text.Component; import org.geysermc.connector.common.ChatColor; import org.geysermc.connector.entity.Entity; +import org.geysermc.connector.entity.EntityDefinitions; import org.geysermc.connector.entity.LivingEntity; 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.network.translators.chat.MessageTranslator; import org.geysermc.connector.scoreboard.Objective; @@ -57,13 +59,11 @@ import org.geysermc.connector.scoreboard.UpdateType; import javax.annotation.Nullable; import java.util.Collections; -import java.util.UUID; import java.util.concurrent.TimeUnit; @Getter @Setter public class PlayerEntity extends LivingEntity { private GameProfile profile; - private UUID uuid; private String username; private boolean playerList = true; // Player is in the player list @@ -76,34 +76,37 @@ public class PlayerEntity extends LivingEntity { */ private ParrotEntity rightParrot; - public PlayerEntity(GameProfile gameProfile, long entityId, long geyserId, Vector3f position, Vector3f motion, Vector3f rotation) { - super(entityId, geyserId, EntityType.PLAYER, position, motion, rotation); + public PlayerEntity(GeyserSession session, long entityId, long geyserId, GameProfile gameProfile, Vector3f position, Vector3f motion, float yaw, float pitch, float headYaw) { + super(session, entityId, geyserId, gameProfile.getId(), EntityDefinitions.PLAYER, position, motion, yaw, pitch, headYaw); profile = gameProfile; - uuid = gameProfile.getId(); username = gameProfile.getName(); - - // For the OptionalPack, set all bits as invisible by default as this matches Java Edition behavior - metadata.put(EntityData.MARK_VARIANT, 0xff); } @Override - public void spawnEntity(GeyserSession session) { + protected void initializeMetadata() { + super.initializeMetadata(); + // For the OptionalPack, set all bits as invisible by default as this matches Java Edition behavior + dirtyMetadata.put(EntityData.MARK_VARIANT, 0xff); + } + + @Override + public void spawnEntity() { // Check to see if the player should have a belowname counterpart added Objective objective = session.getWorldCache().getScoreboard().getObjectiveSlots().get(ScoreboardPosition.BELOW_NAME); if (objective != null) { - setBelowNameText(session, objective); + setBelowNameText(objective); } // The name can't be updated later (the entity metadata for it is ignored), so we need to check for this now - updateDisplayName(session, null, false); + updateDisplayName(null, false); AddPlayerPacket addPlayerPacket = new AddPlayerPacket(); addPlayerPacket.setUuid(uuid); addPlayerPacket.setUsername(username); addPlayerPacket.setRuntimeEntityId(geyserId); addPlayerPacket.setUniqueEntityId(geyserId); - addPlayerPacket.setPosition(position.sub(0, EntityType.PLAYER.getOffset(), 0)); + addPlayerPacket.setPosition(position.sub(0, definition.offset(), 0)); addPlayerPacket.setRotation(getBedrockRotation()); addPlayerPacket.setMotion(motion); addPlayerPacket.setHand(hand); @@ -111,7 +114,11 @@ public class PlayerEntity extends LivingEntity { addPlayerPacket.getAdventureSettings().setPlayerPermission(PlayerPermission.MEMBER); addPlayerPacket.setDeviceId(""); addPlayerPacket.setPlatformChatId(""); - addPlayerPacket.getMetadata().putAll(metadata); + addPlayerPacket.getMetadata().putAll(dirtyMetadata); + addPlayerPacket.getMetadata().putFlags(flags); + + dirtyMetadata.clear(); + setFlagsDirty(false); long linkedEntityId = session.getEntityCache().getCachedPlayerEntityLink(entityId); if (linkedEntityId != -1) { @@ -125,21 +132,23 @@ public class PlayerEntity extends LivingEntity { session.sendUpstreamPacket(addPlayerPacket); } - public void sendPlayer(GeyserSession session) { + public void sendPlayer() { if (session.getEntityCache().getPlayerEntity(uuid) == null) return; if (session.getEntityCache().getEntityByGeyserId(geyserId) == null) { session.getEntityCache().spawnEntity(this); } else { - spawnEntity(session); + spawnEntity(); } } @Override - public void moveAbsolute(GeyserSession session, Vector3f position, Vector3f rotation, boolean isOnGround, boolean teleported) { + public void moveAbsolute(Vector3f position, float yaw, float pitch, float headYaw, boolean isOnGround, boolean teleported) { setPosition(position); - setRotation(rotation); + setYaw(yaw); + setPitch(pitch); + setHeadYaw(headYaw); setOnGround(isOnGround); @@ -156,16 +165,18 @@ public class PlayerEntity extends LivingEntity { session.sendUpstreamPacket(movePlayerPacket); if (leftParrot != null) { - leftParrot.moveAbsolute(session, position, rotation, true, teleported); + leftParrot.moveAbsolute(position, yaw, pitch, headYaw, true, teleported); } if (rightParrot != null) { - rightParrot.moveAbsolute(session, position, rotation, true, teleported); + rightParrot.moveAbsolute(position, yaw, pitch, headYaw, true, teleported); } } @Override - public void moveRelative(GeyserSession session, double relX, double relY, double relZ, Vector3f rotation, boolean isOnGround) { - setRotation(rotation); + public void moveRelative(double relX, double relY, double relZ, float yaw, float pitch, float headYaw, boolean isOnGround) { + setYaw(yaw); + setPitch(pitch); + setHeadYaw(headYaw); this.position = Vector3f.from(position.getX() + relX, position.getY() + relY, position.getZ() + relZ); setOnGround(isOnGround); @@ -178,27 +189,27 @@ public class PlayerEntity extends LivingEntity { movePlayerPacket.setMode(MovePlayerPacket.Mode.NORMAL); // If the player is moved while sleeping, we have to adjust their y, so it appears // correctly on Bedrock. This fixes GSit's lay. - if (metadata.getFlags().getFlag(EntityFlag.SLEEPING)) { - Vector3i bedPosition = metadata.getPos(EntityData.BED_POSITION); + if (dirtyMetadata.getFlags().getFlag(EntityFlag.SLEEPING)) { + Vector3i bedPosition = dirtyMetadata.getPos(EntityData.BED_POSITION); if (bedPosition != null && (bedPosition.getY() == 0 || bedPosition.distanceSquared(position.toInt()) > 4)) { // Force the player movement by using a teleport - movePlayerPacket.setPosition(Vector3f.from(position.getX(), position.getY() - entityType.getOffset() + 0.2f, position.getZ())); + movePlayerPacket.setPosition(Vector3f.from(position.getX(), position.getY() - definition.offset() + 0.2f, position.getZ())); movePlayerPacket.setMode(MovePlayerPacket.Mode.TELEPORT); movePlayerPacket.setTeleportationCause(MovePlayerPacket.TeleportationCause.UNKNOWN); } } session.sendUpstreamPacket(movePlayerPacket); if (leftParrot != null) { - leftParrot.moveRelative(session, relX, relY, relZ, rotation, true); + leftParrot.moveRelative(relX, relY, relZ, yaw, pitch, headYaw, true); } if (rightParrot != null) { - rightParrot.moveRelative(session, relX, relY, relZ, rotation, true); + rightParrot.moveRelative(relX, relY, relZ, yaw, pitch, headYaw, true); } } @Override - public void updateHeadLookRotation(GeyserSession session, float headYaw) { - moveRelative(session, 0, 0, 0, Vector3f.from(rotation.getX(), rotation.getY(), headYaw), onGround); + public void updateHeadLookRotation(float headYaw) { + moveRelative(0, 0, 0, yaw, pitch, headYaw, onGround); MovePlayerPacket movePlayerPacket = new MovePlayerPacket(); movePlayerPacket.setRuntimeEntityId(geyserId); movePlayerPacket.setPosition(position); @@ -208,19 +219,19 @@ public class PlayerEntity extends LivingEntity { } @Override - public void updatePositionAndRotation(GeyserSession session, double moveX, double moveY, double moveZ, float yaw, float pitch, boolean isOnGround) { - moveRelative(session, moveX, moveY, moveZ, yaw, pitch, isOnGround); + public void updatePositionAndRotation(double moveX, double moveY, double moveZ, float yaw, float pitch, boolean isOnGround) { + moveRelative(moveX, moveY, moveZ, yaw, pitch, isOnGround); if (leftParrot != null) { - leftParrot.moveRelative(session, moveX, moveY, moveZ, yaw, pitch, isOnGround); + leftParrot.moveRelative(moveX, moveY, moveZ, yaw, pitch, isOnGround); } if (rightParrot != null) { - rightParrot.moveRelative(session, moveX, moveY, moveZ, yaw, pitch, isOnGround); + rightParrot.moveRelative(moveX, moveY, moveZ, yaw, pitch, isOnGround); } } @Override - public void updateRotation(GeyserSession session, float yaw, float pitch, boolean isOnGround) { - super.updateRotation(session, yaw, pitch, isOnGround); + public void updateRotation(float yaw, float pitch, boolean isOnGround) { + super.updateRotation(yaw, pitch, isOnGround); // Both packets need to be sent or else player head rotation isn't correctly updated MovePlayerPacket movePlayerPacket = new MovePlayerPacket(); movePlayerPacket.setRuntimeEntityId(geyserId); @@ -230,86 +241,90 @@ public class PlayerEntity extends LivingEntity { movePlayerPacket.setMode(MovePlayerPacket.Mode.HEAD_ROTATION); session.sendUpstreamPacket(movePlayerPacket); if (leftParrot != null) { - leftParrot.updateRotation(session, yaw, pitch, isOnGround); + leftParrot.updateRotation(yaw, pitch, isOnGround); } if (rightParrot != null) { - rightParrot.updateRotation(session, yaw, pitch, isOnGround); + rightParrot.updateRotation(yaw, pitch, isOnGround); } } @Override public void setPosition(Vector3f position) { - super.setPosition(position.add(0, entityType.getOffset(), 0)); + super.setPosition(position.add(0, definition.offset(), 0)); } - @Override - public void updateBedrockMetadata(EntityMetadata entityMetadata, GeyserSession session) { - super.updateBedrockMetadata(entityMetadata, session); - + public void setAbsorptionHearts(EntityMetadata entityMetadata) { // Extra hearts - is not metadata but an attribute on Bedrock - if (entityMetadata.getId() == 15) { - UpdateAttributesPacket attributesPacket = new UpdateAttributesPacket(); - attributesPacket.setRuntimeEntityId(geyserId); - // Setting to a higher maximum since plugins/datapacks can probably extend the Bedrock soft limit - attributesPacket.setAttributes(Collections.singletonList( - new AttributeData("minecraft:absorption", 0.0f, 1024f, (float) entityMetadata.getValue(), 0.0f))); - session.sendUpstreamPacket(attributesPacket); - } + UpdateAttributesPacket attributesPacket = new UpdateAttributesPacket(); + attributesPacket.setRuntimeEntityId(geyserId); + // Setting to a higher maximum since plugins/datapacks can probably extend the Bedrock soft limit + attributesPacket.setAttributes(Collections.singletonList( + new AttributeData("minecraft:absorption", 0.0f, 1024f, ((FloatEntityMetadata) entityMetadata).getPrimitiveValue(), 0.0f))); + session.sendUpstreamPacket(attributesPacket); + } - if (entityMetadata.getId() == 17) { - // OptionalPack usage for toggling skin bits - // In Java Edition, a bit being set means that part should be enabled - // However, to ensure that the pack still works on other servers, we invert the bit so all values by default - // are true (0). - metadata.put(EntityData.MARK_VARIANT, ~((byte) entityMetadata.getValue()) & 0xff); - } + public void setSkinVisibility(EntityMetadata entityMetadata) { + // OptionalPack usage for toggling skin bits + // In Java Edition, a bit being set means that part should be enabled + // However, to ensure that the pack still works on other servers, we invert the bit so all values by default + // are true (0). + dirtyMetadata.put(EntityData.MARK_VARIANT, ~((ByteEntityMetadata) entityMetadata).getPrimitiveValue() & 0xff); + } - // Parrot occupying shoulder - if (entityMetadata.getId() == 19 || entityMetadata.getId() == 20) { - CompoundTag tag = (CompoundTag) entityMetadata.getValue(); - boolean isLeft = entityMetadata.getId() == 19; - if (tag != null && !tag.isEmpty()) { - if ((isLeft && leftParrot != null) || (!isLeft && rightParrot != null)) { - // No need to update a parrot's data when it already exists - return; - } - // The parrot is a separate entity in Bedrock, but part of the player entity in Java - ParrotEntity parrot = new ParrotEntity(0, session.getEntityCache().getNextEntityId().incrementAndGet(), - EntityType.PARROT, position, motion, rotation); - parrot.spawnEntity(session); - parrot.getMetadata().put(EntityData.VARIANT, tag.get("Variant").getValue()); - // Different position whether the parrot is left or right - float offset = isLeft ? 0.4f : -0.4f; - parrot.getMetadata().put(EntityData.RIDER_SEAT_POSITION, Vector3f.from(offset, -0.22, -0.1)); - parrot.getMetadata().put(EntityData.RIDER_ROTATION_LOCKED, 1); - parrot.updateBedrockMetadata(session); - SetEntityLinkPacket linkPacket = new SetEntityLinkPacket(); - EntityLinkData.Type type = isLeft ? EntityLinkData.Type.RIDER : EntityLinkData.Type.PASSENGER; - linkPacket.setEntityLink(new EntityLinkData(geyserId, parrot.getGeyserId(), type, false, false)); - // Delay, or else spawned-in players won't get the link - // TODO: Find a better solution. This problem also exists with item frames - session.scheduleInEventLoop(() -> session.sendUpstreamPacket(linkPacket), 500, TimeUnit.MILLISECONDS); - if (isLeft) { - leftParrot = parrot; - } else { - rightParrot = parrot; - } + public void setLeftParrot(EntityMetadata entityMetadata) { + setParrot(entityMetadata.getValue(), true); + } + + public void setRightParrot(EntityMetadata entityMetadata) { + setParrot(entityMetadata.getValue(), false); + } + + /** + * Sets the parrot occupying the shoulder. Bedrock Edition requires a full entity whereas Java Edition just + * spawns it from the NBT data provided + */ + private void setParrot(CompoundTag tag, boolean isLeft) { + if (tag != null && !tag.isEmpty()) { + if ((isLeft && leftParrot != null) || (!isLeft && rightParrot != null)) { + // No need to update a parrot's data when it already exists + return; + } + // The parrot is a separate entity in Bedrock, but part of the player entity in Java //TODO is a UUID provided in NBT? + ParrotEntity parrot = new ParrotEntity(session, 0, session.getEntityCache().getNextEntityId().incrementAndGet(), + null, EntityDefinitions.PARROT, position, motion, yaw, pitch, headYaw); + parrot.spawnEntity(); + parrot.getDirtyMetadata().put(EntityData.VARIANT, tag.get("Variant").getValue()); + // Different position whether the parrot is left or right + float offset = isLeft ? 0.4f : -0.4f; + parrot.getDirtyMetadata().put(EntityData.RIDER_SEAT_POSITION, Vector3f.from(offset, -0.22, -0.1)); + parrot.getDirtyMetadata().put(EntityData.RIDER_ROTATION_LOCKED, 1); + parrot.updateBedrockMetadata(); + SetEntityLinkPacket linkPacket = new SetEntityLinkPacket(); + EntityLinkData.Type type = isLeft ? EntityLinkData.Type.RIDER : EntityLinkData.Type.PASSENGER; + linkPacket.setEntityLink(new EntityLinkData(geyserId, parrot.getGeyserId(), type, false, false)); + // Delay, or else spawned-in players won't get the link + // TODO: Find a better solution. + session.scheduleInEventLoop(() -> session.sendUpstreamPacket(linkPacket), 500, TimeUnit.MILLISECONDS); + if (isLeft) { + leftParrot = parrot; } else { - Entity parrot = isLeft ? leftParrot : rightParrot; - if (parrot != null) { - parrot.despawnEntity(session); - if (isLeft) { - leftParrot = null; - } else { - rightParrot = null; - } + rightParrot = parrot; + } + } else { + Entity parrot = isLeft ? leftParrot : rightParrot; + if (parrot != null) { + parrot.despawnEntity(); + if (isLeft) { + leftParrot = null; + } else { + rightParrot = null; } } } } @Override - protected void setDisplayName(GeyserSession session, Component name) { + public void setDisplayName(EntityMetadata entityMetadata) { // Doesn't do anything for players } @@ -317,7 +332,7 @@ public class PlayerEntity extends LivingEntity { /** * @param useGivenTeam even if there is no team, update the username in the entity metadata anyway, and don't look for a team */ - public void updateDisplayName(GeyserSession session, @Nullable Team team, boolean useGivenTeam) { + public void updateDisplayName(@Nullable Team team, boolean useGivenTeam) { if (team == null && !useGivenTeam) { // Only search for the team if we are not supposed to use the given team // If the given team is null, this is intentional that we are being removed from the team @@ -343,12 +358,12 @@ public class PlayerEntity extends LivingEntity { // The name is not visible to the session player; clear name newDisplayName = ""; } - needsUpdate = useGivenTeam && !newDisplayName.equals(metadata.getString(EntityData.NAMETAG, null)); - metadata.put(EntityData.NAMETAG, newDisplayName); + needsUpdate = useGivenTeam && !newDisplayName.equals(dirtyMetadata.getString(EntityData.NAMETAG, null)); + dirtyMetadata.put(EntityData.NAMETAG, newDisplayName); } else if (useGivenTeam) { // The name has reset, if it was previously something else - needsUpdate = !newDisplayName.equals(metadata.getString(EntityData.NAMETAG)); - metadata.put(EntityData.NAMETAG, this.username); + needsUpdate = !newDisplayName.equals(dirtyMetadata.getString(EntityData.NAMETAG)); + dirtyMetadata.put(EntityData.NAMETAG, this.username); } else { needsUpdate = false; } @@ -363,7 +378,7 @@ public class PlayerEntity extends LivingEntity { } @Override - protected void setDisplayNameVisible(EntityMetadata entityMetadata) { + public void setDisplayNameVisible(EntityMetadata entityMetadata) { // Doesn't do anything for players } @@ -378,11 +393,13 @@ public class PlayerEntity extends LivingEntity { return; } } - metadata.put(EntityData.BOUNDING_BOX_WIDTH, entityType.getWidth()); - metadata.put(EntityData.BOUNDING_BOX_HEIGHT, height); + if (height != boundingBoxHeight || definition.width() != boundingBoxWidth) { + dirtyMetadata.put(EntityData.BOUNDING_BOX_WIDTH, definition.width()); + dirtyMetadata.put(EntityData.BOUNDING_BOX_HEIGHT, height); + } } - public void setBelowNameText(GeyserSession session, Objective objective) { + public void setBelowNameText(Objective objective) { if (objective != null && objective.getUpdateType() != UpdateType.REMOVE) { int amount; Score score = objective.getScores().get(username); @@ -393,7 +410,7 @@ public class PlayerEntity extends LivingEntity { } String displayString = amount + " " + objective.getDisplayName(); - metadata.put(EntityData.SCORE_TAG, displayString); + dirtyMetadata.put(EntityData.SCORE_TAG, displayString); if (valid) { // Already spawned - we still need to run the rest of this code because the spawn packet will be // providing the information @@ -405,7 +422,7 @@ public class PlayerEntity extends LivingEntity { } else { // Always remove the score tag first, then check for valid. // That way the score tag is removed if the player was spawned, then despawned, and is being respawned - if (metadata.remove(EntityData.SCORE_TAG) != null && valid) { + if (dirtyMetadata.remove(EntityData.SCORE_TAG) != null && valid) { SetEntityDataPacket packet = new SetEntityDataPacket(); packet.setRuntimeEntityId(geyserId); packet.getMetadata().put(EntityData.SCORE_TAG, ""); diff --git a/connector/src/main/java/org/geysermc/connector/entity/player/SessionPlayerEntity.java b/connector/src/main/java/org/geysermc/connector/entity/player/SessionPlayerEntity.java index 373ff6f43..d8a3e5492 100644 --- a/connector/src/main/java/org/geysermc/connector/entity/player/SessionPlayerEntity.java +++ b/connector/src/main/java/org/geysermc/connector/entity/player/SessionPlayerEntity.java @@ -30,6 +30,7 @@ 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.github.steveice10.mc.protocol.data.game.entity.metadata.type.ByteEntityMetadata; import com.nukkitx.math.vector.Vector3f; import com.nukkitx.protocol.bedrock.data.AttributeData; import com.nukkitx.protocol.bedrock.data.entity.EntityFlag; @@ -62,21 +63,21 @@ public class SessionPlayerEntity extends PlayerEntity { private final GeyserSession session; public SessionPlayerEntity(GeyserSession session) { - super(new GameProfile(UUID.randomUUID(), "unknown"), 1, 1, Vector3f.ZERO, Vector3f.ZERO, Vector3f.ZERO); + super(session, 1, 1, new GameProfile(UUID.randomUUID(), "unknown"), Vector3f.ZERO, Vector3f.ZERO, 0, 0, 0); valid = true; this.session = session; } @Override - public void spawnEntity(GeyserSession session) { + public void spawnEntity() { // Already logged in } @Override - public void moveRelative(GeyserSession session, double relX, double relY, double relZ, Vector3f rotation, boolean isOnGround) { - super.moveRelative(session, relX, relY, relZ, rotation, isOnGround); - session.getCollisionManager().updatePlayerBoundingBox(this.position.down(entityType.getOffset())); + public void moveRelative(double relX, double relY, double relZ, float yaw, float pitch, float headYaw, boolean isOnGround) { + super.moveRelative(relX, relY, relZ, yaw, pitch, headYaw, isOnGround); + session.getCollisionManager().updatePlayerBoundingBox(this.position.down(definition.offset())); } @Override @@ -99,24 +100,25 @@ public class SessionPlayerEntity extends PlayerEntity { } @Override - public void updateBedrockMetadata(EntityMetadata entityMetadata, GeyserSession session) { - super.updateBedrockMetadata(entityMetadata, session); - if (entityMetadata.getId() == 0) { - session.setSwimmingInWater((((byte) entityMetadata.getValue()) & 0x10) == 0x10 && metadata.getFlags().getFlag(EntityFlag.SPRINTING)); - refreshSpeed = true; - } else if (entityMetadata.getId() == 6) { - session.setPose((Pose) entityMetadata.getValue()); - refreshSpeed = true; - } + public void setFlags(EntityMetadata entityMetadata) { + super.setFlags(entityMetadata); + session.setSwimmingInWater((((ByteEntityMetadata) entityMetadata).getPrimitiveValue() & 0x10) == 0x10 && getFlag(EntityFlag.SPRINTING)); + refreshSpeed = true; + } + + @Override + public void setPose(EntityMetadata entityMetadata) { + super.setPose(entityMetadata); + session.setPose(entityMetadata.getValue()); + refreshSpeed = true; } public float getMaxHealth() { return maxHealth; } - @Override public void setHealth(float health) { - super.setHealth(health); + this.health = health; } @Override @@ -138,8 +140,8 @@ public class SessionPlayerEntity extends PlayerEntity { } @Override - public void updateBedrockMetadata(GeyserSession session) { - super.updateBedrockMetadata(session); + public void updateBedrockMetadata() { + super.updateBedrockMetadata(); if (refreshSpeed) { AttributeData speedAttribute = session.adjustSpeed(); if (speedAttribute != null) { diff --git a/connector/src/main/java/org/geysermc/connector/entity/player/SkullPlayerEntity.java b/connector/src/main/java/org/geysermc/connector/entity/player/SkullPlayerEntity.java index 61b5af38e..c2c41d0dc 100644 --- a/connector/src/main/java/org/geysermc/connector/entity/player/SkullPlayerEntity.java +++ b/connector/src/main/java/org/geysermc/connector/entity/player/SkullPlayerEntity.java @@ -34,7 +34,6 @@ import com.nukkitx.protocol.bedrock.data.entity.EntityData; import com.nukkitx.protocol.bedrock.data.entity.EntityFlag; import com.nukkitx.protocol.bedrock.packet.AddPlayerPacket; import lombok.Getter; -import org.geysermc.connector.entity.type.EntityType; import org.geysermc.connector.network.session.GeyserSession; /** @@ -49,31 +48,34 @@ public class SkullPlayerEntity extends PlayerEntity { @Getter private final int blockState; - public SkullPlayerEntity(GameProfile gameProfile, long geyserId, Vector3f position, Vector3f rotation, int blockState) { - super(gameProfile, 0, geyserId, position, Vector3f.ZERO, rotation); + public SkullPlayerEntity(GeyserSession session, long geyserId, GameProfile gameProfile, Vector3f position, float rotation, int blockState) { + super(session, 0, geyserId, gameProfile, position, Vector3f.ZERO, rotation, 0, rotation); this.blockState = blockState; setPlayerList(false); + } - //Set bounding box to almost nothing so the skull is able to be broken and not cause entity to cast a shadow - metadata.clear(); - metadata.put(EntityData.SCALE, 1.08f); - metadata.put(EntityData.BOUNDING_BOX_HEIGHT, 0.001f); - metadata.put(EntityData.BOUNDING_BOX_WIDTH, 0.001f); - metadata.getOrCreateFlags().setFlag(EntityFlag.CAN_SHOW_NAME, false); - metadata.getFlags().setFlag(EntityFlag.INVISIBLE, true); // Until the skin is loaded + @Override + protected void initializeMetadata() { + // Deliberately do not call super + // Set bounding box to almost nothing so the skull is able to be broken and not cause entity to cast a shadow + dirtyMetadata.put(EntityData.SCALE, 1.08f); + dirtyMetadata.put(EntityData.BOUNDING_BOX_HEIGHT, 0.001f); + dirtyMetadata.put(EntityData.BOUNDING_BOX_WIDTH, 0.001f); + setFlag(EntityFlag.CAN_SHOW_NAME, false); + setFlag(EntityFlag.INVISIBLE, true); // Until the skin is loaded } /** * Overwritten so each entity doesn't check for a linked entity */ @Override - public void spawnEntity(GeyserSession session) { + public void spawnEntity() { AddPlayerPacket addPlayerPacket = new AddPlayerPacket(); addPlayerPacket.setUuid(getUuid()); addPlayerPacket.setUsername(getUsername()); addPlayerPacket.setRuntimeEntityId(geyserId); addPlayerPacket.setUniqueEntityId(geyserId); - addPlayerPacket.setPosition(position.sub(0, EntityType.PLAYER.getOffset(), 0)); + addPlayerPacket.setPosition(position.sub(0, definition.offset(), 0)); addPlayerPacket.setRotation(getBedrockRotation()); addPlayerPacket.setMotion(motion); addPlayerPacket.setHand(hand); @@ -81,14 +83,18 @@ public class SkullPlayerEntity extends PlayerEntity { addPlayerPacket.getAdventureSettings().setPlayerPermission(PlayerPermission.MEMBER); addPlayerPacket.setDeviceId(""); addPlayerPacket.setPlatformChatId(""); - addPlayerPacket.getMetadata().putAll(metadata); + addPlayerPacket.getMetadata().putAll(dirtyMetadata); + addPlayerPacket.getMetadata().putFlags(flags); + + dirtyMetadata.clear(); + setFlagsDirty(false); valid = true; session.sendUpstreamPacket(addPlayerPacket); } - public void despawnEntity(GeyserSession session, Vector3i position) { - this.despawnEntity(session); + public void despawnEntity(Vector3i position) { + this.despawnEntity(); session.getSkullCache().remove(position, this); } } diff --git a/connector/src/main/java/org/geysermc/connector/entity/type/EntityType.java b/connector/src/main/java/org/geysermc/connector/entity/type/EntityType.java index 85f6fde5b..517864e14 100644 --- a/connector/src/main/java/org/geysermc/connector/entity/type/EntityType.java +++ b/connector/src/main/java/org/geysermc/connector/entity/type/EntityType.java @@ -26,222 +26,8 @@ package org.geysermc.connector.entity.type; import lombok.Getter; -import org.geysermc.connector.entity.*; -import org.geysermc.connector.entity.living.*; -import org.geysermc.connector.entity.living.animal.*; -import org.geysermc.connector.entity.living.animal.horse.*; -import org.geysermc.connector.entity.living.animal.tameable.CatEntity; -import org.geysermc.connector.entity.living.animal.tameable.ParrotEntity; -import org.geysermc.connector.entity.living.animal.tameable.WolfEntity; -import org.geysermc.connector.entity.living.merchant.AbstractMerchantEntity; -import org.geysermc.connector.entity.living.merchant.VillagerEntity; -import org.geysermc.connector.entity.living.monster.*; -import org.geysermc.connector.entity.living.monster.raid.*; -import org.geysermc.connector.entity.player.PlayerEntity; - -import java.util.ArrayList; -import java.util.List; -import java.util.Locale; @Getter public enum EntityType { - CHICKEN(ChickenEntity.class, 10, 0.7f, 0.4f), - COW(AnimalEntity.class, 11, 1.4f, 0.9f), - PIG(PigEntity.class, 12, 0.9f), - SHEEP(SheepEntity.class, 13, 1.3f, 0.9f), - WOLF(WolfEntity.class, 14, 0.85f, 0.6f), - VILLAGER(VillagerEntity.class, 15, 1.8f, 0.6f, 0.6f, 1.62f, "minecraft:villager_v2"), - MOOSHROOM(MooshroomEntity.class, 16, 1.4f, 0.9f), - SQUID(SquidEntity.class, 17, 0.8f), - RABBIT(RabbitEntity.class, 18, 0.5f, 0.4f), - BAT(BatEntity.class, 19, 0.9f, 0.5f), - IRON_GOLEM(IronGolemEntity.class, 20, 2.7f, 1.4f), - SNOW_GOLEM(SnowGolemEntity.class, 21, 1.9f, 0.7f), - OCELOT(OcelotEntity.class, 22, 0.35f, 0.3f), - HORSE(HorseEntity.class, 23, 1.6f, 1.3965f), - DONKEY(ChestedHorseEntity.class, 24, 1.6f, 1.3965f), - MULE(ChestedHorseEntity.class, 25, 1.6f, 1.3965f), - SKELETON_HORSE(AbstractHorseEntity.class, 26, 1.6f, 1.3965f), - ZOMBIE_HORSE(AbstractHorseEntity.class, 27, 1.6f, 1.3965f), - POLAR_BEAR(PolarBearEntity.class, 28, 1.4f, 1.3f), - LLAMA(LlamaEntity.class, 29, 1.87f, 0.9f), - TRADER_LLAMA(TraderLlamaEntity.class, 29, 1.187f, 0.9f, 0f, 0f, "minecraft:llama"), - PARROT(ParrotEntity.class, 30, 0.9f, 0.5f), - DOLPHIN(WaterEntity.class, 31, 0.6f, 0.9f), - ZOMBIE(ZombieEntity.class, 32, 1.8f, 0.6f, 0.6f, 1.62f), - GIANT(GiantEntity.class, 32, 1.8f, 0.6f, 0.6f, 1.62f, "minecraft:zombie"), - CREEPER(CreeperEntity.class, 33, 1.7f, 0.6f, 0.6f, 1.62f), - SKELETON(SkeletonEntity.class, 34, 1.8f, 0.6f, 0.6f, 1.62f), - SPIDER(SpiderEntity.class, 35, 0.9f, 1.4f, 1.4f, 1f), - ZOMBIFIED_PIGLIN(ZombifiedPiglinEntity.class, 36, 1.95f, 0.6f, 0.6f, 1.62f, "minecraft:zombie_pigman"), - SLIME(SlimeEntity.class, 37, 0.51f), - ENDERMAN(EndermanEntity.class, 38, 2.9f, 0.6f), - SILVERFISH(MonsterEntity.class, 39, 0.3f, 0.4f), - CAVE_SPIDER(MonsterEntity.class, 40, 0.5f, 0.7f), - GHAST(GhastEntity.class, 41, 4.0f), - MAGMA_CUBE(MagmaCubeEntity.class, 42, 0.51f), - BLAZE(BlazeEntity.class, 43, 1.8f, 0.6f), - ZOMBIE_VILLAGER(ZombieVillagerEntity.class, 44, 1.8f, 0.6f, 0.6f, 1.62f, "minecraft:zombie_villager_v2"), - WITCH(RaidParticipantEntity.class, 45, 1.8f, 0.6f, 0.6f, 1.62f), - STRAY(AbstractSkeletonEntity.class, 46, 1.8f, 0.6f, 0.6f, 1.62f), - HUSK(ZombieEntity.class, 47, 1.8f, 0.6f, 0.6f, 1.62f), - WITHER_SKELETON(AbstractSkeletonEntity.class, 48, 2.4f, 0.7f), - GUARDIAN(GuardianEntity.class, 49, 0.85f), - ELDER_GUARDIAN(ElderGuardianEntity.class, 50, 1.9975f), - NPC(PlayerEntity.class, 51, 1.8f, 0.6f, 0.6f, 1.62f), - WITHER(WitherEntity.class, 52, 3.5f, 0.9f), - ENDER_DRAGON(EnderDragonEntity.class, 53, 0f, 0f), - SHULKER(ShulkerEntity.class, 54, 1f, 1f), - ENDERMITE(MonsterEntity.class, 55, 0.3f, 0.4f), - AGENT(Entity.class, 56, 0f), - VINDICATOR(VindicatorEntity.class, 57, 1.8f, 0.6f, 0.6f, 1.62f), - PILLAGER(PillagerEntity.class, 114, 1.8f, 0.6f, 0.6f, 1.62f), - WANDERING_TRADER(AbstractMerchantEntity.class, 118, 1.8f, 0.6f, 0.6f, 1.62f), - PHANTOM(PhantomEntity.class, 58, 0.5f, 0.9f, 0.9f, 0.6f), - RAVAGER(RaidParticipantEntity.class, 59, 1.9f, 1.2f), - - ARMOR_STAND(ArmorStandEntity.class, 61, 1.975f, 0.5f), - TRIPOD_CAMERA(Entity.class, 62, 0f), - PLAYER(PlayerEntity.class, 63, 1.8f, 0.6f, 0.6f, 1.62f), - ITEM(ItemEntity.class, 64, 0.25f, 0.25f, 0.25f, 0.125f), - PRIMED_TNT(TNTEntity.class, 65, 0.98f, 0.98f, 0.98f, 0f, "minecraft:tnt"), - FALLING_BLOCK(FallingBlockEntity.class, 66, 0.98f, 0.98f), - MOVING_BLOCK(Entity.class, 67, 0f), - THROWN_EXP_BOTTLE(ThrowableItemEntity.class, 68, 0.25f, 0.25f, 0f, 0f, "minecraft:xp_bottle"), - EXPERIENCE_ORB(ExpOrbEntity.class, 69, 0f, 0f, 0f, 0f, "minecraft:xp_orb"), - EYE_OF_ENDER(Entity.class, 70, 0.25f, 0.25f, 0f, 0f, "minecraft:eye_of_ender_signal"), - END_CRYSTAL(EnderCrystalEntity.class, 71, 2.0f, 2.0f, 2.0f, 0f, "minecraft:ender_crystal"), - FIREWORK_ROCKET(FireworkEntity.class, 72, 0.25f, 0.25f, 0.25f, 0f, "minecraft:fireworks_rocket"), - TRIDENT(TridentEntity.class, 73, 0f, 0f, 0f, 0f, "minecraft:thrown_trident"), - TURTLE(TurtleEntity.class, 74, 0.4f, 1.2f), - CAT(CatEntity.class, 75, 0.35f, 0.3f), - SHULKER_BULLET(ThrowableEntity.class, 76, 0.3125f), - FISHING_BOBBER(FishingHookEntity.class, 77, 0f, 0f, 0f, 0f, "minecraft:fishing_hook"), - CHALKBOARD(Entity.class, 78, 0f), - DRAGON_FIREBALL(ItemedFireballEntity.class, 79, 1.0f), - ARROW(TippedArrowEntity.class, 80, 0.25f, 0.25f), - SPECTRAL_ARROW(AbstractArrowEntity.class, 80, 0.25f, 0.25f, 0.25f, 0f, "minecraft:arrow"), - SNOWBALL(ThrowableItemEntity.class, 81, 0.25f), - THROWN_EGG(ThrowableItemEntity.class, 82, 0.25f, 0.25f, 0.25f, 0f, "minecraft:egg"), - PAINTING(PaintingEntity.class, 83, 0f), - MINECART(MinecartEntity.class, 84, 0.7f, 0.98f, 0.98f, 0.35f), - FIREBALL(ItemedFireballEntity.class, 85, 1.0f), - THROWN_POTION(ThrownPotionEntity.class, 86, 0.25f, 0.25f, 0.25f, 0f, "minecraft:splash_potion"), - THROWN_ENDERPEARL(ThrowableItemEntity.class, 87, 0.25f, 0.25f, 0.25f, 0f, "minecraft:ender_pearl"), - LEASH_KNOT(LeashKnotEntity.class, 88, 0.5f, 0.375f), - WITHER_SKULL(WitherSkullEntity.class, 89, 0.3125f), - BOAT(BoatEntity.class, 90, 0.6f, 1.6f, 1.6f, 0.35f), - WITHER_SKULL_DANGEROUS(WitherSkullEntity.class, 91, 0f), - LIGHTNING_BOLT(LightningEntity.class, 93, 0f), - SMALL_FIREBALL(ItemedFireballEntity.class, 94, 0.3125f), - AREA_EFFECT_CLOUD(AreaEffectCloudEntity.class, 95, 0.5f, 1.0f), - MINECART_HOPPER(MinecartEntity.class, 96, 0.7f, 0.98f, 0.98f, 0.35f, "minecraft:hopper_minecart"), - MINECART_TNT(MinecartEntity.class, 97, 0.7f, 0.98f, 0.98f, 0.35f, "minecraft:tnt_minecart"), - MINECART_CHEST(MinecartEntity.class, 98, 0.7f, 0.98f, 0.98f, 0.35f, "minecraft:chest_minecart"), - MINECART_FURNACE(FurnaceMinecartEntity.class, 98, 0.7f, 0.98f, 0.98f, 0.35f, "minecraft:minecart"), - MINECART_SPAWNER(SpawnerMinecartEntity.class, 98, 0.7f, 0.98f, 0.98f, 0.35f, "minecraft:minecart"), - MINECART_COMMAND_BLOCK(CommandBlockMinecartEntity.class, 100, 0.7f, 0.98f, 0.98f, 0.35f, "minecraft:command_block_minecart"), - LINGERING_POTION(ThrowableEntity.class, 101, 0f), - LLAMA_SPIT(ThrowableEntity.class, 102, 0.25f), - EVOKER_FANGS(Entity.class, 103, 0.8f, 0.5f, 0.5f, 0f, "minecraft:evocation_fang"), - EVOKER(SpellcasterIllagerEntity.class, 104, 1.95f, 0.6f, 0.6f, 0f, "minecraft:evocation_illager"), - VEX(VexEntity.class, 105, 0.8f, 0.4f), - ICE_BOMB(Entity.class, 106, 0f), - BALLOON(Entity.class, 107, 0f), - PUFFERFISH(PufferFishEntity.class, 108, 0.7f, 0.7f), - SALMON(AbstractFishEntity.class, 109, 0.5f, 0.7f), - DROWNED(ZombieEntity.class, 110, 1.95f, 0.6f), - TROPICAL_FISH(TropicalFishEntity.class, 111, 0.6f, 0.6f, 0f, 0f, "minecraft:tropicalfish"), - COD(AbstractFishEntity.class, 112, 0.25f, 0.5f), - PANDA(PandaEntity.class, 113, 1.25f, 1.125f, 1.825f), - FOX(FoxEntity.class, 121, 0.5f, 1.25f), - BEE(BeeEntity.class, 122, 0.6f, 0.6f), - STRIDER(StriderEntity.class, 125, 1.7f, 0.9f, 0f, 0f, "minecraft:strider"), - HOGLIN(HoglinEntity.class, 124, 1.4f, 1.3965f, 1.3965f, 0f, "minecraft:hoglin"), - ZOGLIN(ZoglinEntity.class, 126, 1.4f, 1.3965f, 1.3965f, 0f, "minecraft:zoglin"), - PIGLIN(PiglinEntity.class, 123, 1.95f, 0.6f, 0.6f, 0f, "minecraft:piglin"), - PIGLIN_BRUTE(BasePiglinEntity.class, 127, 1.95f, 0.6f, 0.6f, 0f, "minecraft:piglin_brute"), - AXOLOTL(AxolotlEntity.class, 0, 0.42f, 0.7f, 0.7f, 0f, "minecraft:axolotl"), - GLOW_SQUID(GlowSquidEntity.class, 0, 0.8f, 0.8f, 0.8f, 0f, "minecraft:glow_squid"), - GOAT(GoatEntity.class, 0, 1.3f, 0.9f, 0.9f, 0f, "minecraft:goat"), - MARKER(Entity.class, 0, 0, 0, 0, 0, "minecraft:marker"), // Only should be used for ALL_JAVA_IDENTIFIERS - - /** - * Item frames are handled differently since they are a block in Bedrock. - */ - ITEM_FRAME(ItemFrameEntity.class, 0, 0, 0), - GLOW_ITEM_FRAME(ItemFrameEntity.class, 0, 0, 0), - - /** - * Not an entity in Bedrock, so we replace it with an evoker - */ - ILLUSIONER(SpellcasterIllagerEntity.class, 104, 1.8f, 0.6f, 0.6f, 1.62f, "minecraft:evocation_illager"), - - /** - * Not an entity in Bedrock, but used for the Ender Dragon's multiple hitboxes - */ - ENDER_DRAGON_PART(EnderDragonPartEntity.class, 32, 0, 0, 0, 0, "minecraft:armor_stand"); - - /** - * A list of all Java identifiers for use with command suggestions - */ - public static final String[] ALL_JAVA_IDENTIFIERS; - private static final EntityType[] VALUES = values(); - - static { - List allJavaIdentifiers = new ArrayList<>(); - for (EntityType type : VALUES) { - if (type == AGENT || type == BALLOON || type == CHALKBOARD || type == NPC || type == TRIPOD_CAMERA || type == ENDER_DRAGON_PART) { - continue; - } - allJavaIdentifiers.add("minecraft:" + type.name().toLowerCase(Locale.ROOT)); - } - ALL_JAVA_IDENTIFIERS = allJavaIdentifiers.toArray(new String[0]); - } - - private final Class entityClass; - private final int type; - private final float height; - private final float width; - private final float length; - private final float offset; - private final String identifier; - - EntityType(Class entityClass, int type, float height) { - //noinspection SuspiciousNameCombination - this(entityClass, type, height, height); - } - - EntityType(Class entityClass, int type, float height, float width) { - this(entityClass, type, height, width, width); - } - - EntityType(Class entityClass, int type, float height, float width, float length) { - this(entityClass, type, height, width, length, 0f); - } - - EntityType(Class entityClass, int type, float height, float width, float length, float offset) { - this(entityClass, type, height, width, length, offset, null); - } - - EntityType(Class entityClass, int type, float height, float width, float length, float offset, String identifier) { - this.entityClass = entityClass; - this.type = type; - this.height = height; - this.width = width; - this.length = length; - this.offset = offset + 0.00001f; - this.identifier = identifier == null ? "minecraft:" + name().toLowerCase() : identifier; - } - - public static EntityType getFromIdentifier(String identifier) { - for (EntityType type : VALUES) { - if (type.identifier.equals(identifier)) { - return type; - } - } - - return null; - } } diff --git a/connector/src/main/java/org/geysermc/connector/network/session/GeyserSession.java b/connector/src/main/java/org/geysermc/connector/network/session/GeyserSession.java index 33df29f9f..ef545746b 100644 --- a/connector/src/main/java/org/geysermc/connector/network/session/GeyserSession.java +++ b/connector/src/main/java/org/geysermc/connector/network/session/GeyserSession.java @@ -532,7 +532,7 @@ public class GeyserSession implements CommandSender { upstream.sendPacket(biomeDefinitionListPacket); AvailableEntityIdentifiersPacket entityPacket = new AvailableEntityIdentifiersPacket(); - entityPacket.setIdentifiers(Registries.ENTITY_IDENTIFIERS.get()); + entityPacket.setIdentifiers(Registries.BEDROCK_ENTITY_IDENTIFIERS.get()); upstream.sendPacket(entityPacket); CreativeContentPacket creativePacket = new CreativeContentPacket(); @@ -999,7 +999,7 @@ public class GeyserSession implements CommandSender { for (Tickable entity : entityCache.getTickableEntities()) { - entity.tick(this); + entity.tick(); } } catch (Throwable throwable) { throwable.printStackTrace(); @@ -1030,7 +1030,7 @@ public class GeyserSession implements CommandSender { collisionManager.updateScaffoldingFlags(false); } - playerEntity.updateBedrockMetadata(this); + playerEntity.updateBedrockMetadata(); if (mouseoverEntity != null) { // Horses, etc can change their property depending on if you're sneaking @@ -1040,17 +1040,17 @@ public class GeyserSession implements CommandSender { private void setSneakingPose(boolean sneaking) { this.pose = sneaking ? Pose.SNEAKING : Pose.STANDING; - playerEntity.getMetadata().put(EntityData.BOUNDING_BOX_HEIGHT, sneaking ? 1.5f : playerEntity.getEntityType().getHeight()); - playerEntity.getMetadata().getFlags().setFlag(EntityFlag.SNEAKING, sneaking); + playerEntity.getDirtyMetadata().put(EntityData.BOUNDING_BOX_HEIGHT, sneaking ? 1.5f : playerEntity.getDefinition().height()); + playerEntity.setFlag(EntityFlag.SNEAKING, sneaking); collisionManager.updatePlayerBoundingBox(); } public void setSwimming(boolean swimming) { this.pose = swimming ? Pose.SWIMMING : Pose.STANDING; - playerEntity.getMetadata().put(EntityData.BOUNDING_BOX_HEIGHT, swimming ? 0.6f : playerEntity.getEntityType().getHeight()); - playerEntity.getMetadata().getFlags().setFlag(EntityFlag.SWIMMING, swimming); - playerEntity.updateBedrockMetadata(this); + playerEntity.getDirtyMetadata().put(EntityData.BOUNDING_BOX_HEIGHT, swimming ? 0.6f : playerEntity.getDefinition().height()); + playerEntity.setFlag(EntityFlag.SWIMMING, swimming); + playerEntity.updateBedrockMetadata(); } public void setFlying(boolean flying) { @@ -1059,7 +1059,7 @@ public class GeyserSession implements CommandSender { if (sneaking) { // update bounding box as it is not reduced when flying setSneakingPose(!flying); - playerEntity.updateBedrockMetadata(this); + playerEntity.updateBedrockMetadata(); } } @@ -1072,7 +1072,7 @@ public class GeyserSession implements CommandSender { 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())) { + (!swimmingInWater && playerEntity.getDirtyMetadata().getFlags().getFlag(EntityFlag.SWIMMING) && !collisionManager.isPlayerInWater())) { // Either of those conditions means that Bedrock goes zoom when they shouldn't be AttributeData speedAttribute = GeyserAttributeType.MOVEMENT_SPEED.getAttribute(originalSpeedAttribute / 3.32f); playerEntity.getAttributes().put(GeyserAttributeType.MOVEMENT_SPEED, speedAttribute); @@ -1282,7 +1282,7 @@ public class GeyserSession implements CommandSender { if (resendID != -1) { connector.getLogger().debug("Resending teleport " + resendID); TeleportCache teleport = teleportMap.get(resendID); - getPlayerEntity().moveAbsolute(this, Vector3f.from(teleport.getX(), teleport.getY(), teleport.getZ()), + getPlayerEntity().moveAbsolute(Vector3f.from(teleport.getX(), teleport.getY(), teleport.getZ()), teleport.getYaw(), teleport.getPitch(), playerEntity.isOnGround(), true); } } diff --git a/connector/src/main/java/org/geysermc/connector/network/session/cache/EntityCache.java b/connector/src/main/java/org/geysermc/connector/network/session/cache/EntityCache.java index 3ff547c95..10b7159aa 100644 --- a/connector/src/main/java/org/geysermc/connector/network/session/cache/EntityCache.java +++ b/connector/src/main/java/org/geysermc/connector/network/session/cache/EntityCache.java @@ -31,7 +31,6 @@ import it.unimi.dsi.fastutil.longs.Long2ObjectMap; import it.unimi.dsi.fastutil.longs.Long2ObjectOpenHashMap; import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap; import it.unimi.dsi.fastutil.objects.ObjectArrayList; -import it.unimi.dsi.fastutil.objects.ObjectOpenHashSet; import lombok.Getter; import org.geysermc.connector.entity.Entity; import org.geysermc.connector.entity.Tickable; @@ -46,8 +45,6 @@ import java.util.concurrent.atomic.AtomicLong; * for that player (e.g. seeing vanished players from /vanish) */ public class EntityCache { - private final GeyserSession session; - @Getter private final Long2ObjectMap entities = new Long2ObjectOpenHashMap<>(); /** @@ -63,13 +60,12 @@ public class EntityCache { private final AtomicLong nextEntityId = new AtomicLong(2L); public EntityCache(GeyserSession session) { - this.session = session; cachedPlayerEntityLinks.defaultReturnValue(-1L); } public void spawnEntity(Entity entity) { if (cacheEntity(entity)) { - entity.spawnEntity(session); + entity.spawnEntity(); if (entity instanceof Tickable) { // Start ticking it @@ -89,7 +85,7 @@ public class EntityCache { } public boolean removeEntity(Entity entity, boolean force) { - if (entity != null && entity.isValid() && (force || entity.despawnEntity(session))) { + if (entity != null && entity.isValid() && (force || entity.despawnEntity())) { long geyserId = entityIdTranslations.remove(entity.getEntityId()); entities.remove(geyserId); @@ -102,9 +98,9 @@ public class EntityCache { } public void removeAllEntities() { - List entities = new ArrayList<>(session.getEntityCache().getEntities().values()); + List entities = new ArrayList<>(this.entities.values()); for (Entity entity : entities) { - session.getEntityCache().removeEntity(entity, false); + removeEntity(entity, false); } // As a precaution @@ -119,16 +115,6 @@ public class EntityCache { return entities.get(entityIdTranslations.get(javaId)); } - public Set getEntitiesByType(Class entityType) { - Set entitiesOfType = new ObjectOpenHashSet<>(); - for (Entity entity : (entityType == PlayerEntity.class ? playerEntities : entities).values()) { - if (entity.is(entityType)) { - entitiesOfType.add(entity.as(entityType)); - } - } - return entitiesOfType; - } - public void addPlayerEntity(PlayerEntity entity) { playerEntities.put(entity.getUuid(), entity); } diff --git a/connector/src/main/java/org/geysermc/connector/network/session/cache/PistonCache.java b/connector/src/main/java/org/geysermc/connector/network/session/cache/PistonCache.java index a0bed9ad4..14efca357 100644 --- a/connector/src/main/java/org/geysermc/connector/network/session/cache/PistonCache.java +++ b/connector/src/main/java/org/geysermc/connector/network/session/cache/PistonCache.java @@ -121,7 +121,7 @@ public class PistonCache { SessionPlayerEntity playerEntity = session.getPlayerEntity(); boolean isOnGround = playerDisplacement.getY() > 0 || playerEntity.isOnGround(); Vector3d position = session.getCollisionManager().getPlayerBoundingBox().getBottomCenter(); - playerEntity.moveAbsolute(session, position.toFloat(), playerEntity.getRotation(), isOnGround, true); + playerEntity.moveAbsolute(position.toFloat(), playerEntity.getYaw(), playerEntity.getPitch(), playerEntity.getHeadYaw(), isOnGround, true); } } diff --git a/connector/src/main/java/org/geysermc/connector/network/session/cache/WorldBorder.java b/connector/src/main/java/org/geysermc/connector/network/session/cache/WorldBorder.java index f312bb8b4..9d51f82d2 100644 --- a/connector/src/main/java/org/geysermc/connector/network/session/cache/WorldBorder.java +++ b/connector/src/main/java/org/geysermc/connector/network/session/cache/WorldBorder.java @@ -33,8 +33,8 @@ import com.nukkitx.protocol.bedrock.packet.LevelEventPacket; import com.nukkitx.protocol.bedrock.packet.PlayerFogPacket; import lombok.Getter; import lombok.Setter; +import org.geysermc.connector.entity.EntityDefinitions; import org.geysermc.connector.entity.player.PlayerEntity; -import org.geysermc.connector.entity.type.EntityType; import org.geysermc.connector.network.session.GeyserSession; import javax.annotation.Nonnull; @@ -150,9 +150,8 @@ public class WorldBorder { PlayerEntity playerEntity = session.getPlayerEntity(); // Move the player back, but allow gravity to take place // Teleported = true makes going back better, but disconnects the player from their mounted entity - playerEntity.moveAbsolute(session, - Vector3f.from(playerEntity.getPosition().getX(), (newPosition.getY() - EntityType.PLAYER.getOffset()), playerEntity.getPosition().getZ()), - playerEntity.getRotation(), playerEntity.isOnGround(), session.getRidingVehicleEntity() == null); + playerEntity.moveAbsolute(Vector3f.from(playerEntity.getPosition().getX(), (newPosition.getY() - EntityDefinitions.PLAYER.offset()), playerEntity.getPosition().getZ()), + playerEntity.getYaw(), playerEntity.getPitch(), playerEntity.getHeadYaw(), playerEntity.isOnGround(), session.getRidingVehicleEntity() == null); } return isInWorldBorder; } @@ -261,7 +260,7 @@ public class WorldBorder { } private void drawWall(Vector3f position, boolean drawWallX) { - int initialY = (int) (position.getY() - EntityType.PLAYER.getOffset() - 1); + int initialY = (int) (position.getY() - EntityDefinitions.PLAYER.offset() - 1); for (int y = initialY; y < (initialY + 5); y++) { if (drawWallX) { float x = position.getX(); diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/bedrock/BedrockAdventureSettingsTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/bedrock/BedrockAdventureSettingsTranslator.java index 8a978fac3..10138f9a3 100644 --- a/connector/src/main/java/org/geysermc/connector/network/translators/bedrock/BedrockAdventureSettingsTranslator.java +++ b/connector/src/main/java/org/geysermc/connector/network/translators/bedrock/BedrockAdventureSettingsTranslator.java @@ -50,7 +50,7 @@ public class BedrockAdventureSettingsTranslator extends PacketTranslator { // Include type of boat in the name - int variant = entity.getMetadata().getInt(EntityData.VARIANT); + int variant = ((BoatEntity) entity).getVariant(); String typeOfBoat = switch (variant) { case 1 -> "spruce"; case 2 -> "birch"; @@ -65,15 +67,16 @@ public class BedrockEntityPickRequestTranslator extends PacketTranslator itemName = "lead"; case MINECART_CHEST, MINECART_COMMAND_BLOCK, MINECART_FURNACE, MINECART_HOPPER, MINECART_TNT -> - // Move MINECART to the end of the name - itemName = entity.getEntityType().toString().toLowerCase().replace("minecart_", "") + "_minecart"; + // The Bedrock identifier matches the item name which moves MINECART to the end of the name + // TODO test + itemName = entity.getDefinition().identifier(); case MINECART_SPAWNER -> itemName = "minecart"; // Turns into a normal minecart //case ITEM_FRAME -> Not an entity in Bedrock Edition //case GLOW_ITEM_FRAME -> case ARMOR_STAND, END_CRYSTAL, MINECART, PAINTING -> // No spawn egg, just an item - itemName = entity.getEntityType().toString().toLowerCase(); - default -> itemName = entity.getEntityType().toString().toLowerCase() + "_spawn_egg"; + itemName = entity.getDefinition().entityType().toString().toLowerCase(Locale.ROOT); + default -> itemName = entity.getDefinition().entityType().toString().toLowerCase(Locale.ROOT) + "_spawn_egg"; } String fullItemName = "minecraft:" + itemName; diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/bedrock/BedrockInventoryTransactionTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/bedrock/BedrockInventoryTransactionTranslator.java index 06d9a69ee..baa679abc 100644 --- a/connector/src/main/java/org/geysermc/connector/network/translators/bedrock/BedrockInventoryTransactionTranslator.java +++ b/connector/src/main/java/org/geysermc/connector/network/translators/bedrock/BedrockInventoryTransactionTranslator.java @@ -26,11 +26,11 @@ package org.geysermc.connector.network.translators.bedrock; import com.github.steveice10.mc.protocol.data.game.entity.metadata.Position; +import com.github.steveice10.mc.protocol.data.game.entity.object.Direction; import com.github.steveice10.mc.protocol.data.game.entity.player.GameMode; import com.github.steveice10.mc.protocol.data.game.entity.player.Hand; import com.github.steveice10.mc.protocol.data.game.entity.player.InteractAction; import com.github.steveice10.mc.protocol.data.game.entity.player.PlayerAction; -import com.github.steveice10.mc.protocol.data.game.level.block.BlockFace; import com.github.steveice10.mc.protocol.packet.ingame.serverbound.player.ServerboundInteractPacket; import com.github.steveice10.mc.protocol.packet.ingame.serverbound.player.ServerboundPlayerActionPacket; import com.github.steveice10.mc.protocol.packet.ingame.serverbound.player.ServerboundUseItemOnPacket; @@ -42,8 +42,8 @@ import com.nukkitx.protocol.bedrock.data.inventory.*; import com.nukkitx.protocol.bedrock.packet.*; import org.geysermc.connector.entity.CommandBlockMinecartEntity; import org.geysermc.connector.entity.Entity; +import org.geysermc.connector.entity.EntityDefinitions; import org.geysermc.connector.entity.ItemFrameEntity; -import org.geysermc.connector.entity.type.EntityType; import org.geysermc.connector.inventory.GeyserItemStack; import org.geysermc.connector.network.session.GeyserSession; import org.geysermc.connector.network.translators.PacketTranslator; @@ -92,7 +92,7 @@ public class BedrockInventoryTransactionTranslator extends PacketTranslator - playerPosition = playerPosition.sub(0, (EntityType.PLAYER.getOffset() - 1.27f), 0); + playerPosition = playerPosition.sub(0, (EntityDefinitions.PLAYER.offset() - 1.27f), 0); case SWIMMING, FALL_FLYING, // Elytra SPIN_ATTACK -> // Trident spin attack - playerPosition = playerPosition.sub(0, (EntityType.PLAYER.getOffset() - 0.4f), 0); + playerPosition = playerPosition.sub(0, (EntityDefinitions.PLAYER.offset() - 0.4f), 0); case SLEEPING -> - playerPosition = playerPosition.sub(0, (EntityType.PLAYER.getOffset() - 0.2f), 0); + playerPosition = playerPosition.sub(0, (EntityDefinitions.PLAYER.offset() - 0.2f), 0); } // else, we don't have to modify the position float diffX = playerPosition.getX() - packet.getBlockPosition().getX(); @@ -175,7 +175,7 @@ public class BedrockInventoryTransactionTranslator extends PacketTranslator MAXIMUM_BLOCK_DESTROYING_DISTANCE) { @@ -325,7 +325,7 @@ public class BedrockInventoryTransactionTranslator extends PacketTranslator 0) { sendMovement = true; } @@ -75,16 +75,15 @@ public class BedrockPlayerInputTranslator extends PacketTranslator= 100) { Vector3f vehiclePosition = vehicle.getPosition(); - Vector3f vehicleRotation = vehicle.getRotation(); if (vehicle instanceof BoatEntity) { // Remove some Y position to prevents boats flying up - vehiclePosition = vehiclePosition.down(EntityType.BOAT.getOffset()); + vehiclePosition = vehiclePosition.down(EntityDefinitions.BOAT.offset()); } ServerboundMoveVehiclePacket moveVehiclePacket = new ServerboundMoveVehiclePacket( vehiclePosition.getX(), vehiclePosition.getY(), vehiclePosition.getZ(), - vehicleRotation.getX() - 90, vehicleRotation.getY() + vehicle.getYaw() - 90, vehicle.getPitch() ); session.sendDownstreamPacket(moveVehiclePacket); } diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/bedrock/BedrockRespawnTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/bedrock/BedrockRespawnTranslator.java index 8a70ada6f..8d1fda4e8 100644 --- a/connector/src/main/java/org/geysermc/connector/network/translators/bedrock/BedrockRespawnTranslator.java +++ b/connector/src/main/java/org/geysermc/connector/network/translators/bedrock/BedrockRespawnTranslator.java @@ -57,7 +57,7 @@ public class BedrockRespawnTranslator extends PacketTranslator { if (entity == null) return; SetEntityDataPacket entityDataPacket = new SetEntityDataPacket(); entityDataPacket.setRuntimeEntityId(entity.getGeyserId()); - entityDataPacket.getMetadata().putAll(entity.getMetadata()); + entityDataPacket.getMetadata().putAll(entity.getDirtyMetadata()); session.sendUpstreamPacket(entityDataPacket); MovePlayerPacket movePlayerPacket = new MovePlayerPacket(); diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/bedrock/entity/BedrockEntityEventTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/bedrock/entity/BedrockEntityEventTranslator.java index 9b25a6486..f46f486e9 100644 --- a/connector/src/main/java/org/geysermc/connector/network/translators/bedrock/entity/BedrockEntityEventTranslator.java +++ b/connector/src/main/java/org/geysermc/connector/network/translators/bedrock/entity/BedrockEntityEventTranslator.java @@ -62,8 +62,8 @@ public class BedrockEntityEventTranslator extends PacketTranslator= 0 && packet.getData() < trades.length) { VillagerTrade trade = merchantInventory.getVillagerTrades()[packet.getData()]; openInventory.setItem(2, GeyserItemStack.from(trade.getOutput()), session); - villager.getMetadata().put(EntityData.TRADE_XP, trade.getXp() + villager.getMetadata().getInt(EntityData.TRADE_XP)); - villager.updateBedrockMetadata(session); + villager.getDirtyMetadata().put(EntityData.TRADE_XP, trade.getXp() + villager.getDirtyMetadata().getInt(EntityData.TRADE_XP)); + villager.updateBedrockMetadata(); } } }, 100, TimeUnit.MILLISECONDS); diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/bedrock/entity/player/BedrockActionTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/bedrock/entity/player/BedrockActionTranslator.java index 497de45f8..cbc01f8ee 100644 --- a/connector/src/main/java/org/geysermc/connector/network/translators/bedrock/entity/player/BedrockActionTranslator.java +++ b/connector/src/main/java/org/geysermc/connector/network/translators/bedrock/entity/player/BedrockActionTranslator.java @@ -26,8 +26,8 @@ package org.geysermc.connector.network.translators.bedrock.entity.player; import com.github.steveice10.mc.protocol.data.game.entity.metadata.Position; +import com.github.steveice10.mc.protocol.data.game.entity.object.Direction; import com.github.steveice10.mc.protocol.data.game.entity.player.*; -import com.github.steveice10.mc.protocol.data.game.level.block.BlockFace; import com.github.steveice10.mc.protocol.packet.ingame.serverbound.player.*; import com.nukkitx.math.vector.Vector3f; import com.nukkitx.math.vector.Vector3i; @@ -116,7 +116,7 @@ public class BedrockActionTranslator extends PacketTranslator { if (session.getConnector().getConfig().getEmoteOffhandWorkaround() != EmoteOffhandWorkaroundOption.DISABLED) { // Activate the workaround - we should trigger the offhand now ServerboundPlayerActionPacket swapHandsPacket = new ServerboundPlayerActionPacket(PlayerAction.SWAP_HANDS, BlockUtils.POSITION_ZERO, - BlockFace.DOWN); + Direction.DOWN); session.sendDownstreamPacket(swapHandsPacket); if (session.getConnector().getConfig().getEmoteOffhandWorkaround() == EmoteOffhandWorkaroundOption.NO_EMOTES) { diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/bedrock/entity/player/BedrockInteractTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/bedrock/entity/player/BedrockInteractTranslator.java index 9a307d076..fbbc9ffc6 100644 --- a/connector/src/main/java/org/geysermc/connector/network/translators/bedrock/entity/player/BedrockInteractTranslator.java +++ b/connector/src/main/java/org/geysermc/connector/network/translators/bedrock/entity/player/BedrockInteractTranslator.java @@ -90,8 +90,8 @@ public class BedrockInteractTranslator extends PacketTranslator if (session.getMouseoverEntity() != null) { // No interactive tag should be sent session.setMouseoverEntity(null); - session.getPlayerEntity().getMetadata().put(EntityData.INTERACTIVE_TAG, ""); - session.getPlayerEntity().updateBedrockMetadata(session); + session.getPlayerEntity().getDirtyMetadata().put(EntityData.INTERACTIVE_TAG, ""); + session.getPlayerEntity().updateBedrockMetadata(); } } break; @@ -99,7 +99,7 @@ public class BedrockInteractTranslator extends PacketTranslator if (session.getOpenInventory() == null) { Entity ridingEntity = session.getRidingVehicleEntity(); if (ridingEntity instanceof AbstractHorseEntity) { - if (ridingEntity.getMetadata().getFlags().getFlag(EntityFlag.TAMED)) { + if (ridingEntity.getDirtyMetadata().getFlags().getFlag(EntityFlag.TAMED)) { // We should request to open the horse inventory instead ServerboundPlayerCommandPacket openHorseWindowPacket = new ServerboundPlayerCommandPacket((int) session.getPlayerEntity().getEntityId(), PlayerState.OPEN_HORSE_INVENTORY); session.sendDownstreamPacket(openHorseWindowPacket); diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/bedrock/entity/player/BedrockMovePlayerTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/bedrock/entity/player/BedrockMovePlayerTranslator.java index 85d3a945e..609ee6f2d 100644 --- a/connector/src/main/java/org/geysermc/connector/network/translators/bedrock/entity/player/BedrockMovePlayerTranslator.java +++ b/connector/src/main/java/org/geysermc/connector/network/translators/bedrock/entity/player/BedrockMovePlayerTranslator.java @@ -34,8 +34,8 @@ import com.nukkitx.math.vector.Vector3f; import com.nukkitx.protocol.bedrock.packet.MoveEntityAbsolutePacket; import com.nukkitx.protocol.bedrock.packet.MovePlayerPacket; import org.geysermc.connector.common.ChatColor; +import org.geysermc.connector.entity.EntityDefinitions; import org.geysermc.connector.entity.player.SessionPlayerEntity; -import org.geysermc.connector.entity.type.EntityType; import org.geysermc.connector.network.session.GeyserSession; import org.geysermc.connector.network.translators.PacketTranslator; import org.geysermc.connector.network.translators.Translator; @@ -68,14 +68,17 @@ public class BedrockMovePlayerTranslator extends PacketTranslator INCORRECT_MOVEMENT_THRESHOLD) { PlayerEntity playerEntity = session.getPlayerEntity(); if (pistonCache.getPlayerMotion().equals(Vector3f.ZERO) && !pistonCache.isPlayerSlimeCollision()) { - playerEntity.moveAbsolute(session, position.toFloat(), playerEntity.getRotation(), newOnGround, true); + playerEntity.moveAbsolute(position.toFloat(), playerEntity.getYaw(), playerEntity.getPitch(), playerEntity.getHeadYaw(), newOnGround, true); } } @@ -190,7 +190,7 @@ public class CollisionManager { // Gravity might need to be reset... SetEntityDataPacket entityDataPacket = new SetEntityDataPacket(); entityDataPacket.setRuntimeEntityId(entity.getGeyserId()); - entityDataPacket.getMetadata().putAll(entity.getMetadata()); + entityDataPacket.getMetadata().putAll(entity.getDirtyMetadata()); session.sendUpstreamPacket(entityDataPacket); MovePlayerPacket movePlayerPacket = new MovePlayerPacket(); @@ -376,9 +376,9 @@ public class CollisionManager { // at the current location. double originalY = playerBoundingBox.getMiddleY(); double originalHeight = playerBoundingBox.getSizeY(); - double standingY = originalY - (originalHeight / 2.0) + (EntityType.PLAYER.getHeight() / 2.0); + double standingY = originalY - (originalHeight / 2.0) + (EntityDefinitions.PLAYER.height() / 2.0); - playerBoundingBox.setSizeY(EntityType.PLAYER.getHeight()); + playerBoundingBox.setSizeY(EntityDefinitions.PLAYER.height()); playerBoundingBox.setMiddleY(standingY); boolean result = collision.checkIntersection(position, playerBoundingBox); result |= session.getPistonCache().checkCollision(position, playerBoundingBox); @@ -403,18 +403,17 @@ public class CollisionManager { * @param updateMetadata whether we should update metadata if something changed */ public void updateScaffoldingFlags(boolean updateMetadata) { - EntityFlags flags = session.getPlayerEntity().getMetadata().getFlags(); - boolean flagsChanged; + Entity entity = session.getPlayerEntity(); boolean isSneakingWithScaffolding = (touchingScaffolding || onScaffolding) && session.isSneaking(); - flagsChanged = flags.setFlag(EntityFlag.OVER_DESCENDABLE_BLOCK, onScaffolding); - flagsChanged |= flags.setFlag(EntityFlag.IN_ASCENDABLE_BLOCK, touchingScaffolding); - flagsChanged |= flags.setFlag(EntityFlag.OVER_SCAFFOLDING, isSneakingWithScaffolding); + entity.setFlag(EntityFlag.OVER_DESCENDABLE_BLOCK, onScaffolding); + entity.setFlag(EntityFlag.IN_ASCENDABLE_BLOCK, touchingScaffolding); + entity.setFlag(EntityFlag.OVER_SCAFFOLDING, isSneakingWithScaffolding); - flagsChanged |= flags.setFlag(EntityFlag.IN_SCAFFOLDING, touchingScaffolding); + entity.setFlag(EntityFlag.IN_SCAFFOLDING, touchingScaffolding); - if (flagsChanged && updateMetadata) { - session.getPlayerEntity().updateBedrockMetadata(session); + if (updateMetadata) { + session.getPlayerEntity().updateBedrockMetadata(); } } } diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/inventory/translators/MerchantInventoryTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/inventory/translators/MerchantInventoryTranslator.java index 5f20a9d03..be3b09b50 100644 --- a/connector/src/main/java/org/geysermc/connector/network/translators/inventory/translators/MerchantInventoryTranslator.java +++ b/connector/src/main/java/org/geysermc/connector/network/translators/inventory/translators/MerchantInventoryTranslator.java @@ -28,7 +28,6 @@ package org.geysermc.connector.network.translators.inventory.translators; import com.github.steveice10.mc.protocol.data.game.inventory.ContainerType; import com.nukkitx.math.vector.Vector3f; import com.nukkitx.protocol.bedrock.data.entity.EntityData; -import com.nukkitx.protocol.bedrock.data.entity.EntityDataMap; import com.nukkitx.protocol.bedrock.data.entity.EntityLinkData; import com.nukkitx.protocol.bedrock.data.inventory.ContainerSlotType; import com.nukkitx.protocol.bedrock.data.inventory.ItemStackRequest; @@ -36,7 +35,7 @@ import com.nukkitx.protocol.bedrock.data.inventory.StackRequestSlotInfoData; import com.nukkitx.protocol.bedrock.packet.ItemStackResponsePacket; import com.nukkitx.protocol.bedrock.packet.SetEntityLinkPacket; import org.geysermc.connector.entity.Entity; -import org.geysermc.connector.entity.type.EntityType; +import org.geysermc.connector.entity.EntityDefinitions; import org.geysermc.connector.inventory.Inventory; import org.geysermc.connector.inventory.MerchantContainer; import org.geysermc.connector.inventory.PlayerInventory; @@ -99,14 +98,15 @@ public class MerchantInventoryTranslator extends BaseInventoryTranslator { long geyserId = session.getEntityCache().getNextEntityId().incrementAndGet(); Vector3f pos = session.getPlayerEntity().getPosition().sub(0, 3, 0); - EntityDataMap metadata = new EntityDataMap(); - metadata.put(EntityData.SCALE, 0f); - metadata.put(EntityData.BOUNDING_BOX_WIDTH, 0f); - metadata.put(EntityData.BOUNDING_BOX_HEIGHT, 0f); - - Entity villager = new Entity(0, geyserId, EntityType.VILLAGER, pos, Vector3f.ZERO, Vector3f.ZERO); - villager.setMetadata(metadata); - villager.spawnEntity(session); + Entity villager = new Entity(session, 0, geyserId, null, EntityDefinitions.VILLAGER, pos, Vector3f.ZERO, 0f, 0f, 0f) { + @Override + protected void initializeMetadata() { + dirtyMetadata.put(EntityData.SCALE, 0f); + dirtyMetadata.put(EntityData.BOUNDING_BOX_WIDTH, 0f); + dirtyMetadata.put(EntityData.BOUNDING_BOX_HEIGHT, 0f); + } + }; + villager.spawnEntity(); SetEntityLinkPacket linkPacket = new SetEntityLinkPacket(); EntityLinkData.Type type = EntityLinkData.Type.PASSENGER; @@ -127,7 +127,7 @@ public class MerchantInventoryTranslator extends BaseInventoryTranslator { public void closeInventory(GeyserSession session, Inventory inventory) { MerchantContainer merchantInventory = (MerchantContainer) inventory; if (merchantInventory.getVillager() != null) { - merchantInventory.getVillager().despawnEntity(session); + merchantInventory.getVillager().despawnEntity(); } } diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/java/JavaCommandsTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/java/JavaCommandsTranslator.java index ddc05b62f..8eca69333 100644 --- a/connector/src/main/java/org/geysermc/connector/network/translators/java/JavaCommandsTranslator.java +++ b/connector/src/main/java/org/geysermc/connector/network/translators/java/JavaCommandsTranslator.java @@ -43,12 +43,12 @@ import lombok.Getter; import lombok.ToString; import net.kyori.adventure.text.format.NamedTextColor; import org.geysermc.connector.GeyserConnector; -import org.geysermc.connector.entity.type.EntityType; 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.network.translators.item.Enchantment; import org.geysermc.connector.registry.BlockRegistries; +import org.geysermc.connector.registry.Registries; import org.geysermc.connector.utils.EntityUtils; import java.util.*; @@ -220,7 +220,7 @@ public class JavaCommandsTranslator extends PacketTranslator BlockRegistries.JAVA_TO_BEDROCK_IDENTIFIERS.get().keySet().toArray(new String[0]); case ITEM_STACK -> session.getItemMappings().getItemNames(); case ITEM_ENCHANTMENT -> Enchantment.JavaEnchantment.ALL_JAVA_IDENTIFIERS; - case ENTITY_SUMMON -> EntityType.ALL_JAVA_IDENTIFIERS; + case ENTITY_SUMMON -> Registries.JAVA_ENTITY_IDENTIFIERS.get().keySet().toArray(new String[0]); //TODO add Marker case COLOR -> VALID_COLORS; case SCOREBOARD_SLOT -> VALID_SCOREBOARD_SLOTS; case MOB_EFFECT -> ALL_EFFECT_IDENTIFIERS; diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/java/JavaLoginTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/java/JavaLoginTranslator.java index cbcbe9f1d..ca7c92302 100644 --- a/connector/src/main/java/org/geysermc/connector/network/translators/java/JavaLoginTranslator.java +++ b/connector/src/main/java/org/geysermc/connector/network/translators/java/JavaLoginTranslator.java @@ -92,7 +92,7 @@ public class JavaLoginTranslator extends PacketTranslator { @@ -47,22 +50,30 @@ public class JavaSetEntityDataTranslator extends PacketTranslator> translators = (List>) entity.getDefinition().translators(); + + for (EntityMetadata metadata : packet.getMetadata()) { + if (metadata.getId() >= translators.size()) { + session.getConnector().getLogger().warning("Metadata ID " + metadata.getId() + " is out of bounds of known entity metadata size " + translators.size() + " for entity type " + entity.getDefinition().entityType()); if (session.getConnector().getConfig().isDebugMode()) { - e.printStackTrace(); + session.getConnector().getLogger().debug(metadata.toString()); } + continue; } + + EntityMetadataTranslator translator = (EntityMetadataTranslator) translators.get(metadata.getId()); + if (translator == null) { + // This can safely happen; it means we don't translate this entity metadata + continue; + } + if (translator.acceptedType() != metadata.getType()) { + session.getConnector().getLogger().warning("Metadata ID " + metadata.getId() + " was received with type " + metadata.getType() + " but we expected " + translator.acceptedType() + " for " + entity.getDefinition().entityType()); + continue; + } + translator.translateFunction().accept(entity, metadata); } - entity.updateBedrockMetadata(session); + entity.updateBedrockMetadata(); // Update the interactive tag, if necessary if (session.getMouseoverEntity() != null && session.getMouseoverEntity().getEntityId() == entity.getEntityId()) { diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/java/entity/JavaSetEntityLinkTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/java/entity/JavaSetEntityLinkTranslator.java index 0fd9b0c64..27e0481ee 100644 --- a/connector/src/main/java/org/geysermc/connector/network/translators/java/entity/JavaSetEntityLinkTranslator.java +++ b/connector/src/main/java/org/geysermc/connector/network/translators/java/entity/JavaSetEntityLinkTranslator.java @@ -26,11 +26,11 @@ package org.geysermc.connector.network.translators.java.entity; import com.github.steveice10.mc.protocol.packet.ingame.clientbound.entity.ClientboundSetEntityLinkPacket; -import com.nukkitx.protocol.bedrock.data.entity.EntityData; import com.nukkitx.protocol.bedrock.data.entity.EntityEventType; import com.nukkitx.protocol.bedrock.data.entity.EntityFlag; import com.nukkitx.protocol.bedrock.packet.EntityEventPacket; import org.geysermc.connector.entity.Entity; +import org.geysermc.connector.entity.living.MobEntity; import org.geysermc.connector.network.session.GeyserSession; import org.geysermc.connector.network.translators.PacketTranslator; import org.geysermc.connector.network.translators.Translator; @@ -43,15 +43,14 @@ public class JavaSetEntityLinkTranslator extends PacketTranslator 1)); + EntityUtils.updateMountOffset(passenger, entity, false, false, (packet.getPassengerIds().length > 1)); } else { - EntityUtils.updateMountOffset(passenger, entity, session, (packet.getPassengerIds()[0] == passengerId), true, (packet.getPassengerIds().length > 1)); + EntityUtils.updateMountOffset(passenger, entity, (packet.getPassengerIds()[0] == passengerId), true, (packet.getPassengerIds().length > 1)); } // Force an update to the passenger metadata - passenger.updateBedrockMetadata(session); + passenger.updateBedrockMetadata(); } - switch (entity.getEntityType()) { + switch (entity.getDefinition().entityType()) { case HORSE, SKELETON_HORSE, DONKEY, MULE, RAVAGER -> { - entity.getMetadata().put(EntityData.RIDER_MAX_ROTATION, 181.0f); - entity.updateBedrockMetadata(session); + entity.getDirtyMetadata().put(EntityData.RIDER_MAX_ROTATION, 181.0f); + entity.updateBedrockMetadata(); } } } diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/java/entity/JavaTeleportEntityTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/java/entity/JavaTeleportEntityTranslator.java index 8fbd234a9..89bcb5c0e 100644 --- a/connector/src/main/java/org/geysermc/connector/network/translators/java/entity/JavaTeleportEntityTranslator.java +++ b/connector/src/main/java/org/geysermc/connector/network/translators/java/entity/JavaTeleportEntityTranslator.java @@ -43,6 +43,6 @@ public class JavaTeleportEntityTranslator extends PacketTranslator 1); + EntityUtils.updateMountOffset(entity, vehicle, false, false, entity.getPassengers().size() > 1); } // If coordinates are relative, then add to the existing coordinate double newX = packet.getX() + (packet.getRelative().contains(PositionElement.X) ? entity.getPosition().getX() : 0); double newY = packet.getY() + - (packet.getRelative().contains(PositionElement.Y) ? entity.getPosition().getY() - EntityType.PLAYER.getOffset() : 0); + (packet.getRelative().contains(PositionElement.Y) ? entity.getPosition().getY() - EntityDefinitions.PLAYER.offset() : 0); double newZ = packet.getZ() + (packet.getRelative().contains(PositionElement.Z) ? entity.getPosition().getZ() : 0); @@ -121,16 +123,16 @@ public class JavaPlayerPositionTranslator extends PacketTranslator { @Override public void translate(GeyserSession session, ClientboundAddEntityPacket packet) { - Vector3f position = Vector3f.from(packet.getX(), packet.getY(), packet.getZ()); Vector3f motion = Vector3f.from(packet.getMotionX(), packet.getMotionY(), packet.getMotionZ()); - Vector3f rotation = Vector3f.from(packet.getYaw(), packet.getPitch(), 0); + float yaw = packet.getYaw(); + float pitch = packet.getPitch(); - org.geysermc.connector.entity.type.EntityType type = EntityUtils.toBedrockEntity(packet.getType()); - if (type == null) { + EntityDefinition definition = Registries.ENTITY_DEFINITIONS.get(packet.getType()); + if (definition == null) { session.getConnector().getLogger().warning(LanguageUtils.getLocaleStringLog("geyser.entity.type_null", packet.getType())); return; } - Class entityClass = type.getEntityClass(); - try { - Entity entity; - if (packet.getType() == EntityType.FALLING_BLOCK) { - entity = new FallingBlockEntity(packet.getEntityId(), session.getEntityCache().getNextEntityId().incrementAndGet(), - type, position, motion, rotation, ((FallingBlockData) packet.getData()).getId()); - } else if (packet.getType() == EntityType.ITEM_FRAME || packet.getType() == EntityType.GLOW_ITEM_FRAME) { - // Item frames need the hanging direction - entity = new ItemFrameEntity(packet.getEntityId(), session.getEntityCache().getNextEntityId().incrementAndGet(), - type, position, motion, rotation, (HangingDirection) packet.getData()); - } else if (packet.getType() == EntityType.FISHING_BOBBER) { - // Fishing bobbers need the owner for the line - int ownerEntityId = ((ProjectileData) packet.getData()).getOwnerId(); - Entity owner = session.getEntityCache().getEntityByJavaId(ownerEntityId); - if (owner == null && session.getPlayerEntity().getEntityId() == ownerEntityId) { - owner = session.getPlayerEntity(); - } - // Java clients only spawn fishing hooks with a player as its owner - if (owner instanceof PlayerEntity) { - entity = new FishingHookEntity(packet.getEntityId(), session.getEntityCache().getNextEntityId().incrementAndGet(), - type, position, motion, rotation, (PlayerEntity) owner); - } else { - return; - } - } else if (packet.getType() == EntityType.BOAT) { - // Initial rotation is incorrect - entity = new BoatEntity(packet.getEntityId(), session.getEntityCache().getNextEntityId().incrementAndGet(), - type, position, motion, Vector3f.from(packet.getYaw(), 0, packet.getYaw())); + Entity entity; + if (packet.getType() == EntityType.FALLING_BLOCK) { + entity = new FallingBlockEntity(session, packet.getEntityId(), session.getEntityCache().getNextEntityId().incrementAndGet(), packet.getUuid(), + position, motion, yaw, pitch, ((FallingBlockData) packet.getData()).getId()); + } else if (packet.getType() == EntityType.ITEM_FRAME || packet.getType() == EntityType.GLOW_ITEM_FRAME) { + // Item frames need the hanging direction + entity = new ItemFrameEntity(session, packet.getEntityId(), session.getEntityCache().getNextEntityId().incrementAndGet(), packet.getUuid(), + definition, position, motion, yaw, pitch, (Direction) packet.getData()); + } else if (packet.getType() == EntityType.FISHING_BOBBER) { + // Fishing bobbers need the owner for the line + int ownerEntityId = ((ProjectileData) packet.getData()).getOwnerId(); + Entity owner; + if (session.getPlayerEntity().getEntityId() == ownerEntityId) { + owner = session.getPlayerEntity(); } else { - Constructor entityConstructor = entityClass.getConstructor(long.class, long.class, org.geysermc.connector.entity.type.EntityType.class, - Vector3f.class, Vector3f.class, Vector3f.class); - - entity = entityConstructor.newInstance(packet.getEntityId(), session.getEntityCache().getNextEntityId().incrementAndGet(), - type, position, motion, rotation - ); + owner = session.getEntityCache().getEntityByJavaId(ownerEntityId); } - session.getEntityCache().spawnEntity(entity); - } catch (NoSuchMethodException | InstantiationException | IllegalAccessException | InvocationTargetException ex) { - ex.printStackTrace(); + // Java clients only spawn fishing hooks with a player as its owner + if (owner instanceof PlayerEntity) { + entity = new FishingHookEntity(session, packet.getEntityId(), session.getEntityCache().getNextEntityId().incrementAndGet(), packet.getUuid(), + position, motion, yaw, pitch, (PlayerEntity) owner); + } else { + return; + } + } else { + entity = ((BaseEntityFactory) definition.factory()).create(session, packet.getEntityId(), session.getEntityCache().getNextEntityId().incrementAndGet(), + packet.getUuid(), definition, position, motion, yaw, pitch, 0f); } + session.getEntityCache().spawnEntity(entity); } } diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/java/entity/spawn/JavaAddExperienceOrbTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/java/entity/spawn/JavaAddExperienceOrbTranslator.java index 263170f95..ec2ab1221 100644 --- a/connector/src/main/java/org/geysermc/connector/network/translators/java/entity/spawn/JavaAddExperienceOrbTranslator.java +++ b/connector/src/main/java/org/geysermc/connector/network/translators/java/entity/spawn/JavaAddExperienceOrbTranslator.java @@ -29,7 +29,6 @@ import com.github.steveice10.mc.protocol.packet.ingame.clientbound.entity.spawn. import com.nukkitx.math.vector.Vector3f; import org.geysermc.connector.entity.Entity; import org.geysermc.connector.entity.ExpOrbEntity; -import org.geysermc.connector.entity.type.EntityType; import org.geysermc.connector.network.session.GeyserSession; import org.geysermc.connector.network.translators.PacketTranslator; import org.geysermc.connector.network.translators.Translator; @@ -42,8 +41,7 @@ public class JavaAddExperienceOrbTranslator extends PacketTranslator { @@ -45,25 +43,16 @@ public class JavaAddMobTranslator extends PacketTranslator definition = Registries.ENTITY_DEFINITIONS.get(packet.getType()); + if (definition == null) { session.getConnector().getLogger().warning(LanguageUtils.getLocaleStringLog("geyser.entity.type_null", packet.getType())); return; } - Class entityClass = type.getEntityClass(); - try { - Constructor entityConstructor = entityClass.getConstructor(long.class, long.class, EntityType.class, - Vector3f.class, Vector3f.class, Vector3f.class); - - Entity entity = entityConstructor.newInstance(packet.getEntityId(), session.getEntityCache().getNextEntityId().incrementAndGet(), - type, position, motion, rotation - ); - session.getEntityCache().spawnEntity(entity); - } catch (NoSuchMethodException | InstantiationException | IllegalAccessException | InvocationTargetException ex) { - ex.printStackTrace(); - } + Entity entity = ((BaseEntityFactory) definition.factory()).create(session, packet.getEntityId(), session.getEntityCache().getNextEntityId().incrementAndGet(), + packet.getUuid(), definition, position, motion, packet.getYaw(), packet.getPitch(), packet.getHeadYaw() + ); + session.getEntityCache().spawnEntity(entity); } } diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/java/entity/spawn/JavaAddPaintingTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/java/entity/spawn/JavaAddPaintingTranslator.java index 790e79ecf..4879166a2 100644 --- a/connector/src/main/java/org/geysermc/connector/network/translators/java/entity/spawn/JavaAddPaintingTranslator.java +++ b/connector/src/main/java/org/geysermc/connector/network/translators/java/entity/spawn/JavaAddPaintingTranslator.java @@ -40,9 +40,9 @@ public class JavaAddPaintingTranslator extends PacketTranslator> 4) == packet.getX() && (position.getZ() >> 4) == packet.getZ()) { - session.getSkullCache().get(position).despawnEntity(session); + session.getSkullCache().get(position).despawnEntity(); iterator.remove(); } } diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/java/scoreboard/JavaSetObjectiveTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/java/scoreboard/JavaSetObjectiveTranslator.java index b74d58175..8c1ab9124 100644 --- a/connector/src/main/java/org/geysermc/connector/network/translators/java/scoreboard/JavaSetObjectiveTranslator.java +++ b/connector/src/main/java/org/geysermc/connector/network/translators/java/scoreboard/JavaSetObjectiveTranslator.java @@ -73,7 +73,7 @@ public class JavaSetObjectiveTranslator extends PacketTranslator { if (gameProfile == null) { @@ -117,34 +116,34 @@ public class SkullBlockEntityTranslator extends BlockEntityTranslator implements } if (session.getEventLoop().inEventLoop()) { - spawnPlayer(session, gameProfile, blockPosition, entityPosition, entityRotation, blockState); + spawnPlayer(session, gameProfile, blockPosition, entityPosition, rotation, blockState); } else { - session.executeInEventLoop(() -> spawnPlayer(session, gameProfile, blockPosition, entityPosition, entityRotation, blockState)); + session.executeInEventLoop(() -> spawnPlayer(session, gameProfile, blockPosition, entityPosition, rotation, blockState)); } }); } private static void spawnPlayer(GeyserSession session, GameProfile profile, Vector3i blockPosition, - Vector3f entityPosition, Vector3f entityRotation, int blockState) { + Vector3f entityPosition, float rotation, int blockState) { long geyserId = session.getEntityCache().getNextEntityId().incrementAndGet(); SkullPlayerEntity existingSkull = session.getSkullCache().get(blockPosition); if (existingSkull != null) { // Ensure that two skulls can't spawn on the same point - existingSkull.despawnEntity(session, blockPosition); + existingSkull.despawnEntity(blockPosition); } - SkullPlayerEntity player = new SkullPlayerEntity(profile, geyserId, entityPosition, entityRotation, blockState); + SkullPlayerEntity player = new SkullPlayerEntity(session, geyserId, profile, entityPosition, rotation, blockState); // Cache entity session.getSkullCache().put(blockPosition, player); - player.spawnEntity(session); + player.spawnEntity(); SkullSkinManager.requestAndHandleSkin(player, session, (skin -> session.scheduleInEventLoop(() -> { // Delay to minimize split-second "player" pop-in - player.getMetadata().getFlags().setFlag(EntityFlag.INVISIBLE, false); - player.updateBedrockMetadata(session); + player.setFlag(EntityFlag.INVISIBLE, false); + player.updateBedrockMetadata(); }, 250, TimeUnit.MILLISECONDS))); } } diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/world/block/entity/SpawnerBlockEntityTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/world/block/entity/SpawnerBlockEntityTranslator.java index 579a3196d..88cee13b0 100644 --- a/connector/src/main/java/org/geysermc/connector/network/translators/world/block/entity/SpawnerBlockEntityTranslator.java +++ b/connector/src/main/java/org/geysermc/connector/network/translators/world/block/entity/SpawnerBlockEntityTranslator.java @@ -29,7 +29,8 @@ import com.github.steveice10.mc.protocol.data.game.level.block.BlockEntityType; import com.github.steveice10.opennbt.tag.builtin.CompoundTag; import com.github.steveice10.opennbt.tag.builtin.Tag; import com.nukkitx.nbt.NbtMapBuilder; -import org.geysermc.connector.entity.type.EntityType; +import org.geysermc.connector.entity.EntityDefinition; +import org.geysermc.connector.registry.Registries; @BlockEntity(type = BlockEntityType.MOB_SPAWNER) public class SpawnerBlockEntityTranslator extends BlockEntityTranslator { @@ -72,10 +73,10 @@ public class SpawnerBlockEntityTranslator extends BlockEntityTranslator { .getValue(); builder.put("EntityIdentifier", entityID); - EntityType type = EntityType.getFromIdentifier(entityID); - if (type != null) { - builder.put("DisplayEntityWidth", type.getWidth()); - builder.put("DisplayEntityHeight", type.getHeight()); + EntityDefinition definition = Registries.JAVA_ENTITY_IDENTIFIERS.get(entityID); + if (definition != null) { + builder.put("DisplayEntityWidth", definition.width()); + builder.put("DisplayEntityHeight", definition.height()); builder.put("DisplayEntityScale", 1.0f); } } diff --git a/connector/src/main/java/org/geysermc/connector/registry/Registries.java b/connector/src/main/java/org/geysermc/connector/registry/Registries.java index 157d01ea3..15fbeb03f 100644 --- a/connector/src/main/java/org/geysermc/connector/registry/Registries.java +++ b/connector/src/main/java/org/geysermc/connector/registry/Registries.java @@ -25,11 +25,12 @@ package org.geysermc.connector.registry; +import com.github.steveice10.mc.protocol.data.game.entity.type.EntityType; import com.github.steveice10.mc.protocol.data.game.level.block.BlockEntityType; import com.github.steveice10.mc.protocol.data.game.level.event.SoundEvent; +import com.github.steveice10.mc.protocol.data.game.level.particle.ParticleType; import com.github.steveice10.mc.protocol.data.game.recipe.Recipe; import com.github.steveice10.mc.protocol.data.game.recipe.RecipeType; -import com.github.steveice10.mc.protocol.data.game.level.particle.ParticleType; import com.nukkitx.nbt.NbtMap; import com.nukkitx.protocol.bedrock.data.inventory.CraftingData; import com.nukkitx.protocol.bedrock.data.inventory.PotionMixData; @@ -37,12 +38,14 @@ import it.unimi.dsi.fastutil.Pair; import it.unimi.dsi.fastutil.ints.Int2ObjectMap; import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap; import it.unimi.dsi.fastutil.objects.Object2IntMap; +import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap; +import org.geysermc.connector.entity.EntityDefinition; import org.geysermc.connector.network.translators.collision.translators.BlockCollision; -import org.geysermc.connector.network.translators.world.event.LevelEventTransformer; import org.geysermc.connector.network.translators.item.Enchantment.JavaEnchantment; import org.geysermc.connector.network.translators.sound.SoundHandler; import org.geysermc.connector.network.translators.sound.SoundInteractionHandler; import org.geysermc.connector.network.translators.world.block.entity.BlockEntityTranslator; +import org.geysermc.connector.network.translators.world.event.LevelEventTransformer; import org.geysermc.connector.registry.loader.*; import org.geysermc.connector.registry.populator.ItemRegistryPopulator; import org.geysermc.connector.registry.populator.RecipeRegistryPopulator; @@ -51,6 +54,7 @@ import org.geysermc.connector.registry.type.ItemMappings; import org.geysermc.connector.registry.type.ParticleMapping; import org.geysermc.connector.registry.type.SoundMapping; +import java.util.EnumMap; import java.util.List; import java.util.Map; import java.util.Set; @@ -59,6 +63,11 @@ import java.util.Set; * Holds all the common registries in Geyser. */ public class Registries { + /** + * A registry holding a CompoundTag of the known entity identifiers. + */ + public static final SimpleRegistry BEDROCK_ENTITY_IDENTIFIERS = SimpleRegistry.create("bedrock/entity_identifiers.dat", RegistryLoaders.NBT); + /** * A registry holding a CompoundTag of all the known biomes. */ @@ -90,9 +99,14 @@ public class Registries { public static final SimpleMappedRegistry ENCHANTMENTS; /** - * A registry holding a CompoundTag of the known entity identifiers. + * A map containing all entity types and their respective Geyser definitions */ - public static final SimpleRegistry ENTITY_IDENTIFIERS = SimpleRegistry.create("bedrock/entity_identifiers.dat", RegistryLoaders.NBT); + public static final SimpleMappedRegistry> ENTITY_DEFINITIONS = SimpleMappedRegistry.create(RegistryLoaders.empty(() -> new EnumMap<>(EntityType.class))); + + /** + * A map containing all Java entity identifiers and their respective Geyser definitions + */ + public static final SimpleMappedRegistry> JAVA_ENTITY_IDENTIFIERS = SimpleMappedRegistry.create(RegistryLoaders.empty(Object2ObjectOpenHashMap::new)); /** * A versioned registry which holds {@link ItemMappings} for each version. These item mappings contain diff --git a/connector/src/main/java/org/geysermc/connector/scoreboard/Scoreboard.java b/connector/src/main/java/org/geysermc/connector/scoreboard/Scoreboard.java index 682ff8331..7d0cdea7d 100644 --- a/connector/src/main/java/org/geysermc/connector/scoreboard/Scoreboard.java +++ b/connector/src/main/java/org/geysermc/connector/scoreboard/Scoreboard.java @@ -117,7 +117,7 @@ public final class Scoreboard { continue; } - entity.setBelowNameText(session, objective); + entity.setBelowNameText(objective); } } } @@ -368,7 +368,7 @@ public final class Scoreboard { for (Entity entity : session.getEntityCache().getEntities().values()) { // This more complex logic is for the future to iterate over all entities, not just players if (entity instanceof PlayerEntity player && names.remove(player.getUsername())) { - player.updateDisplayName(session, team, true); + player.updateDisplayName(team, true); if (names.isEmpty()) { break; } @@ -384,7 +384,7 @@ public final class Scoreboard { for (Entity entity : session.getEntityCache().getEntities().values()) { if (entity instanceof PlayerEntity player) { Team playerTeam = session.getWorldCache().getScoreboard().getTeamFor(player.getUsername()); - player.updateDisplayName(session, playerTeam, true); + player.updateDisplayName(playerTeam, true); } } } diff --git a/connector/src/main/java/org/geysermc/connector/utils/BlockUtils.java b/connector/src/main/java/org/geysermc/connector/utils/BlockUtils.java index 86b40093e..140577104 100644 --- a/connector/src/main/java/org/geysermc/connector/utils/BlockUtils.java +++ b/connector/src/main/java/org/geysermc/connector/utils/BlockUtils.java @@ -196,7 +196,7 @@ public class BlockUtils { /** * Given a position, return the position if a block were located on the specified block face. * @param blockPos the block position - * @param face the face of the block - see {@link com.github.steveice10.mc.protocol.data.game.level.block.BlockFace} + * @param face the face of the block - see {@link com.github.steveice10.mc.protocol.data.game.entity.object.Direction} * @return the block position with the block face accounted for */ public static Vector3i getBlockPosition(Vector3i blockPos, int face) { diff --git a/connector/src/main/java/org/geysermc/connector/utils/ChunkUtils.java b/connector/src/main/java/org/geysermc/connector/utils/ChunkUtils.java index 48e1a9622..df2923bb5 100644 --- a/connector/src/main/java/org/geysermc/connector/utils/ChunkUtils.java +++ b/connector/src/main/java/org/geysermc/connector/utils/ChunkUtils.java @@ -152,7 +152,7 @@ public class ChunkUtils { ItemFrameEntity itemFrameEntity = ItemFrameEntity.getItemFrameEntity(session, position); if (itemFrameEntity != null) { if (blockState == JAVA_AIR_ID) { // Item frame is still present and no block overrides that; refresh it - itemFrameEntity.updateBlock(session); + itemFrameEntity.updateBlock(); // Still update the chunk cache with the new block session.getChunkCache().updateBlock(position.getX(), position.getY(), position.getZ(), blockState); return; @@ -163,7 +163,7 @@ public class ChunkUtils { SkullPlayerEntity skull = session.getSkullCache().get(position); if (skull != null && blockState != skull.getBlockState()) { // Skull is gone - skull.despawnEntity(session, position); + skull.despawnEntity(position); } // Prevent moving_piston from being placed diff --git a/connector/src/main/java/org/geysermc/connector/utils/EntityUtils.java b/connector/src/main/java/org/geysermc/connector/utils/EntityUtils.java index fdfb40971..57c0eac73 100644 --- a/connector/src/main/java/org/geysermc/connector/utils/EntityUtils.java +++ b/connector/src/main/java/org/geysermc/connector/utils/EntityUtils.java @@ -26,14 +26,14 @@ package org.geysermc.connector.utils; import com.github.steveice10.mc.protocol.data.game.entity.Effect; +import com.github.steveice10.mc.protocol.data.game.entity.type.EntityType; import com.nukkitx.math.vector.Vector3f; import com.nukkitx.protocol.bedrock.data.entity.EntityData; import com.nukkitx.protocol.bedrock.data.entity.EntityFlag; import org.geysermc.connector.entity.Entity; +import org.geysermc.connector.entity.EntityDefinitions; import org.geysermc.connector.entity.living.ArmorStandEntity; import org.geysermc.connector.entity.living.animal.AnimalEntity; -import org.geysermc.connector.entity.type.EntityType; -import org.geysermc.connector.network.session.GeyserSession; import java.util.Locale; @@ -69,24 +69,10 @@ public final class EntityUtils { }; } - /** - * Converts a MobType to a Bedrock edition EntityType, returns null if the EntityType is not found - * - * @param type The MobType to convert - * @return Converted EntityType - */ - public static EntityType toBedrockEntity(com.github.steveice10.mc.protocol.data.game.entity.type.EntityType type) { - try { - return EntityType.valueOf(type.name()); - } catch (IllegalArgumentException ex) { - return null; - } - } - private static float getMountedHeightOffset(Entity mount) { - float height = mount.getMetadata().getFloat(EntityData.BOUNDING_BOX_HEIGHT); + float height = mount.getDirtyMetadata().getFloat(EntityData.BOUNDING_BOX_HEIGHT); float mountedHeightOffset = height * 0.75f; - switch (mount.getEntityType()) { + switch (mount.getDefinition().entityType()) { case CHICKEN, SPIDER -> mountedHeightOffset = height * 0.5f; case DONKEY, MULE -> mountedHeightOffset -= 0.25f; case LLAMA -> mountedHeightOffset = height * 0.67f; @@ -94,7 +80,7 @@ public final class EntityUtils { MINECART_COMMAND_BLOCK -> mountedHeightOffset = 0; case BOAT -> mountedHeightOffset = -0.1f; case HOGLIN, ZOGLIN -> { - boolean isBaby = mount.getMetadata().getFlags().getFlag(EntityFlag.BABY); + boolean isBaby = mount.getDirtyMetadata().getFlags().getFlag(EntityFlag.BABY); mountedHeightOffset = height - (isBaby ? 0.2f : 0.15f); } case PIGLIN -> mountedHeightOffset = height * 0.92f; @@ -107,7 +93,7 @@ public final class EntityUtils { private static float getHeightOffset(Entity passenger) { boolean isBaby; - switch (passenger.getEntityType()) { + switch (passenger.getDefinition().entityType()) { case SKELETON: case STRAY: case WITHER_SKELETON: @@ -124,10 +110,10 @@ public final class EntityUtils { case PIGLIN: case PIGLIN_BRUTE: case ZOMBIFIED_PIGLIN: - isBaby = passenger.getMetadata().getFlags().getFlag(EntityFlag.BABY); + isBaby = passenger.getDirtyMetadata().getFlags().getFlag(EntityFlag.BABY); return isBaby ? -0.05f : -0.45f; case ZOMBIE: - isBaby = passenger.getMetadata().getFlags().getFlag(EntityFlag.BABY); + isBaby = passenger.getDirtyMetadata().getFlags().getFlag(EntityFlag.BABY); return isBaby ? 0.0f : -0.45f; case EVOKER: case ILLUSIONER: @@ -148,8 +134,8 @@ public final class EntityUtils { /** * Adjust an entity's height if they have mounted/dismounted an entity. */ - public static void updateMountOffset(Entity passenger, Entity mount, GeyserSession session, boolean rider, boolean riding, boolean moreThanOneEntity) { - passenger.getMetadata().getFlags().setFlag(EntityFlag.RIDING, riding); + public static void updateMountOffset(Entity passenger, Entity mount, boolean rider, boolean riding, boolean moreThanOneEntity) { + passenger.setFlag(EntityFlag.RIDING, riding); if (riding) { // Without the Y offset, Bedrock players will find themselves in the floor when mounting float mountedHeightOffset = getMountedHeightOffset(mount); @@ -158,7 +144,7 @@ public final class EntityUtils { float xOffset = 0; float yOffset = mountedHeightOffset + heightOffset; float zOffset = 0; - switch (mount.getEntityType()) { + switch (mount.getDefinition().entityType()) { case BOAT: // Without the X offset, more than one entity on a boat is stacked on top of each other if (rider && moreThanOneEntity) { @@ -180,17 +166,17 @@ public final class EntityUtils { * Horses are tinier * Players, Minecarts, and Boats have different origins */ - if (passenger.getEntityType() == EntityType.PLAYER && mount.getEntityType() != EntityType.PLAYER) { - yOffset += EntityType.PLAYER.getOffset(); + if (passenger.getDefinition().entityType() == EntityType.PLAYER && mount.getDefinition().entityType() != EntityType.PLAYER) { + yOffset += EntityDefinitions.PLAYER.offset(); } - switch (mount.getEntityType()) { + switch (mount.getDefinition().entityType()) { case MINECART, MINECART_HOPPER, MINECART_TNT, MINECART_CHEST, MINECART_FURNACE, MINECART_SPAWNER, - MINECART_COMMAND_BLOCK, BOAT -> yOffset -= mount.getEntityType().getHeight() * 0.5f; + MINECART_COMMAND_BLOCK, BOAT -> yOffset -= mount.getDefinition().height() * 0.5f; } Vector3f offset = Vector3f.from(xOffset, yOffset, zOffset); - passenger.getMetadata().put(EntityData.RIDER_SEAT_POSITION, offset); + passenger.getDirtyMetadata().put(EntityData.RIDER_SEAT_POSITION, offset); } - passenger.updateBedrockMetadata(session); + passenger.updateBedrockMetadata(); } private EntityUtils() { diff --git a/connector/src/main/java/org/geysermc/connector/utils/InteractiveTagManager.java b/connector/src/main/java/org/geysermc/connector/utils/InteractiveTagManager.java index 228a8d448..0f7e66250 100644 --- a/connector/src/main/java/org/geysermc/connector/utils/InteractiveTagManager.java +++ b/connector/src/main/java/org/geysermc/connector/utils/InteractiveTagManager.java @@ -25,14 +25,17 @@ package org.geysermc.connector.utils; +import com.github.steveice10.mc.protocol.data.game.entity.type.EntityType; 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 lombok.Getter; import org.geysermc.connector.entity.Entity; +import org.geysermc.connector.entity.living.MobEntity; import org.geysermc.connector.entity.living.animal.AnimalEntity; import org.geysermc.connector.entity.living.animal.horse.HorseEntity; -import org.geysermc.connector.entity.type.EntityType; +import org.geysermc.connector.entity.living.animal.tameable.CatEntity; +import org.geysermc.connector.entity.living.animal.tameable.WolfEntity; +import org.geysermc.connector.entity.living.merchant.VillagerEntity; import org.geysermc.connector.network.session.GeyserSession; import org.geysermc.connector.registry.type.ItemMapping; @@ -60,26 +63,26 @@ public class InteractiveTagManager { * @param interactEntity the entity that the client is currently facing. */ public static void updateTag(GeyserSession session, Entity interactEntity) { - EntityDataMap entityMetadata = interactEntity.getMetadata(); ItemMapping mapping = session.getPlayerInventory().getItemInHand().getMapping(session); String javaIdentifierStripped = mapping.getJavaIdentifier().replace("minecraft:", ""); + EntityType entityType = interactEntity.getDefinition().entityType(); InteractiveTag interactiveTag = InteractiveTag.NONE; - if (entityMetadata.getLong(EntityData.LEASH_HOLDER_EID) == session.getPlayerEntity().getGeyserId()) { + if (interactEntity instanceof MobEntity mobEntity && mobEntity.getLeashHolderBedrockId() == session.getPlayerEntity().getGeyserId()) { // Unleash the entity interactiveTag = InteractiveTag.REMOVE_LEASH; - } else if (javaIdentifierStripped.equals("saddle") && !entityMetadata.getFlags().getFlag(EntityFlag.SADDLED) && - ((SADDLEABLE_WHEN_TAMED_MOB_TYPES.contains(interactEntity.getEntityType()) && entityMetadata.getFlags().getFlag(EntityFlag.TAMED) && !session.isSneaking()) || - interactEntity.getEntityType() == EntityType.PIG || interactEntity.getEntityType() == EntityType.STRIDER)) { + } else if (javaIdentifierStripped.equals("saddle") && !interactEntity.getFlag(EntityFlag.SADDLED) && + ((SADDLEABLE_WHEN_TAMED_MOB_TYPES.contains(entityType) && interactEntity.getFlag(EntityFlag.TAMED) && !session.isSneaking()) || + entityType == EntityType.PIG || entityType == EntityType.STRIDER)) { // Entity can be saddled and the conditions meet (entity can be saddled and, if needed, is tamed) interactiveTag = InteractiveTag.SADDLE; } else if (javaIdentifierStripped.equals("name_tag") && session.getPlayerInventory().getItemInHand().getNbt() != null && session.getPlayerInventory().getItemInHand().getNbt().contains("display")) { // Holding a named name tag interactiveTag = InteractiveTag.NAME; - } else if (javaIdentifierStripped.equals("lead") && LEASHABLE_MOB_TYPES.contains(interactEntity.getEntityType()) && - entityMetadata.getLong(EntityData.LEASH_HOLDER_EID, -1L) == -1L) { + } else if (interactEntity instanceof MobEntity mobEntity &&javaIdentifierStripped.equals("lead") + && LEASHABLE_MOB_TYPES.contains(entityType) && mobEntity.getLeashHolderBedrockId() == -1L) { // Holding a leash and the mob is leashable for sure // (Plugins can change this behavior so that's something to look into in the far far future) interactiveTag = InteractiveTag.LEASH; @@ -87,17 +90,17 @@ public class InteractiveTagManager { // This animal can be fed interactiveTag = InteractiveTag.FEED; } else { - switch (interactEntity.getEntityType()) { + switch (interactEntity.getDefinition().entityType()) { case BOAT: if (interactEntity.getPassengers().size() < 2) { interactiveTag = InteractiveTag.BOARD_BOAT; } break; case CAT: - if (entityMetadata.getFlags().getFlag(EntityFlag.TAMED) && - entityMetadata.getLong(EntityData.OWNER_EID) == session.getPlayerEntity().getGeyserId()) { + if (interactEntity.getFlag(EntityFlag.TAMED) && + ((CatEntity) interactEntity).getOwnerBedrockId() == session.getPlayerEntity().getGeyserId()) { // Tamed and owned by player - can sit/stand - interactiveTag = entityMetadata.getFlags().getFlag(EntityFlag.SITTING) ? InteractiveTag.STAND : InteractiveTag.SIT; + interactiveTag = interactEntity.getFlag(EntityFlag.SITTING) ? InteractiveTag.STAND : InteractiveTag.SIT; break; } break; @@ -128,7 +131,7 @@ public class InteractiveTagManager { case DONKEY: case LLAMA: case MULE: - if (entityMetadata.getFlags().getFlag(EntityFlag.TAMED) && !entityMetadata.getFlags().getFlag(EntityFlag.CHESTED) + if (interactEntity.getFlag(EntityFlag.TAMED) && !interactEntity.getFlag(EntityFlag.CHESTED) && javaIdentifierStripped.equals("chest")) { // Can attach a chest interactiveTag = InteractiveTag.ATTACH_CHEST; @@ -139,12 +142,12 @@ public class InteractiveTagManager { case SKELETON_HORSE: case TRADER_LLAMA: case ZOMBIE_HORSE: - boolean tamed = entityMetadata.getFlags().getFlag(EntityFlag.TAMED); - if (session.isSneaking() && tamed && (interactEntity instanceof HorseEntity || entityMetadata.getFlags().getFlag(EntityFlag.CHESTED))) { + boolean tamed = interactEntity.getFlag(EntityFlag.TAMED); + if (session.isSneaking() && tamed && (interactEntity instanceof HorseEntity || interactEntity.getFlag(EntityFlag.CHESTED))) { interactiveTag = InteractiveTag.OPEN_CONTAINER; break; } - if (!entityMetadata.getFlags().getFlag(EntityFlag.BABY)) { + if (!interactEntity.getFlag(EntityFlag.BABY)) { // Can't ride a baby if (tamed) { interactiveTag = InteractiveTag.RIDE_HORSE; @@ -165,17 +168,17 @@ public class InteractiveTagManager { interactiveTag = InteractiveTag.OPEN_CONTAINER; break; case PIG: - if (entityMetadata.getFlags().getFlag(EntityFlag.SADDLED)) { + if (interactEntity.getFlag(EntityFlag.SADDLED)) { interactiveTag = InteractiveTag.MOUNT; } break; case PIGLIN: - if (!entityMetadata.getFlags().getFlag(EntityFlag.BABY) && javaIdentifierStripped.equals("gold_ingot")) { + if (!interactEntity.getFlag(EntityFlag.BABY) && javaIdentifierStripped.equals("gold_ingot")) { interactiveTag = InteractiveTag.BARTER; } break; case SHEEP: - if (!entityMetadata.getFlags().getFlag(EntityFlag.SHEARED)) { + if (!interactEntity.getFlag(EntityFlag.SHEARED)) { if (javaIdentifierStripped.equals("shears")) { // Shear the sheep interactiveTag = InteractiveTag.SHEAR; @@ -186,13 +189,13 @@ public class InteractiveTagManager { } break; case STRIDER: - if (entityMetadata.getFlags().getFlag(EntityFlag.SADDLED)) { + if (interactEntity.getFlag(EntityFlag.SADDLED)) { interactiveTag = InteractiveTag.RIDE_STRIDER; } break; case VILLAGER: - if (entityMetadata.getInt(EntityData.VARIANT) != 14 && entityMetadata.getInt(EntityData.VARIANT) != 0 - && entityMetadata.getFloat(EntityData.SCALE) >= 0.75f) { // Not a nitwit, has a profession and is not a baby + VillagerEntity villager = (VillagerEntity) interactEntity; + if (villager.isCanTradeWith() && !villager.isBaby()) { // Not a nitwit, has a profession and is not a baby interactiveTag = InteractiveTag.TRADE; } break; @@ -200,13 +203,13 @@ public class InteractiveTagManager { interactiveTag = InteractiveTag.TRADE; // Since you can always trade with a wandering villager, presumably. break; case WOLF: - if (javaIdentifierStripped.equals("bone") && !entityMetadata.getFlags().getFlag(EntityFlag.TAMED)) { + if (javaIdentifierStripped.equals("bone") && !interactEntity.getFlag(EntityFlag.TAMED)) { // Bone and untamed - can tame interactiveTag = InteractiveTag.TAME; - } else if (entityMetadata.getFlags().getFlag(EntityFlag.TAMED) && - entityMetadata.getLong(EntityData.OWNER_EID) == session.getPlayerEntity().getGeyserId()) { + } else if (interactEntity.getFlag(EntityFlag.TAMED) && + ((WolfEntity) interactEntity).getOwnerBedrockId() == session.getPlayerEntity().getGeyserId()) { // Tamed and owned by player - can sit/stand - interactiveTag = entityMetadata.getFlags().getFlag(EntityFlag.SITTING) ? InteractiveTag.STAND : InteractiveTag.SIT; + interactiveTag = interactEntity.getFlag(EntityFlag.SITTING) ? InteractiveTag.STAND : InteractiveTag.SIT; } break; case ZOMBIE_VILLAGER: @@ -219,15 +222,15 @@ public class InteractiveTagManager { break; } } - session.getPlayerEntity().getMetadata().put(EntityData.INTERACTIVE_TAG, interactiveTag.getValue()); - session.getPlayerEntity().updateBedrockMetadata(session); + session.getPlayerEntity().getDirtyMetadata().put(EntityData.INTERACTIVE_TAG, interactiveTag.getValue()); + session.getPlayerEntity().updateBedrockMetadata(); } /** * All interactive tags in enum form. For potential API usage. */ public enum InteractiveTag { - NONE(true), + NONE((Void) null), IGNITE_CREEPER("creeper"), EDIT, LEAVE_BOAT("exit.boat"), @@ -271,7 +274,7 @@ public class InteractiveTagManager { @Getter private final String value; - InteractiveTag(boolean isNone) { + InteractiveTag(Void isNone) { this.value = ""; }