Entity refactory aaaaaaAAAAAAA

This commit is contained in:
Camotoy 2021-11-17 22:02:38 -05:00
parent 951b616f98
commit 11997ed82b
No known key found for this signature in database
GPG Key ID: 7EEFB66FE798081F
170 changed files with 4003 additions and 2836 deletions

View File

@ -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();

View File

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

View File

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

View File

@ -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<Float> 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<Particle> 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);
}
}

View File

@ -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<Integer> 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<Boolean> 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<Boolean> 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) {

View File

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

View File

@ -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<Integer> 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<Integer> entityMetadata) {
customBlockOffset = ((IntEntityMetadata) entityMetadata).getPrimitiveValue();
if (showCustomBlock) {
dirtyMetadata.put(EntityData.DISPLAY_OFFSET, customBlockOffset);
}
}
@Override
public void setShowCustomBlock(EntityMetadata<Boolean> 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() {
}
}

View File

@ -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<Position> 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);
}
}

View File

@ -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<Byte> 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<Component> 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<Boolean> entityMetadata) {
dirtyMetadata.put(EntityData.NAMETAG_ALWAYS_SHOW, (byte) (((BooleanEntityMetadata) entityMetadata).getPrimitiveValue() ? 1 : 0));
}
public void setGravity(EntityMetadata<Boolean> entityMetadata) {
setFlag(EntityFlag.HAS_GRAVITY, !((BooleanEntityMetadata) entityMetadata).getPrimitiveValue());
}
/**
* Usually used for bounding box and not animation.
*/
public void setPose(EntityMetadata<Pose> 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<Integer> 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")

View File

@ -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 <T> the entity type this definition represents
*/
public record EntityDefinition<T extends Entity>(EntityFactory<T> factory, EntityType entityType, int bedrockId, String identifier,
float width, float height, float offset, List<EntityMetadataTranslator<? super T, ?>> translators) {
public static <T extends Entity> Builder<T> inherited(BaseEntityFactory<T> factory, EntityDefinition<? super T> parent) {
return inherited((EntityFactory<T>) factory, parent);
}
public static <T extends Entity> Builder<T> inherited(EntityFactory<T> factory, EntityDefinition<? super T> parent) {
return new Builder<>(factory, parent.entityType, parent.bedrockId, parent.identifier, parent.width, parent.height, parent.offset, new ObjectArrayList<>(parent.translators));
}
public static <T extends Entity> Builder<T> builder(EntityFactory<T> factory) {
return new Builder<>(factory);
}
@Setter
@Accessors(fluent = true, chain = true)
public static class Builder<T extends Entity> {
private final EntityFactory<T> factory;
private EntityType type;
private int bedrockId;
private String identifier;
private float width;
private float height;
private float offset;
private final List<EntityMetadataTranslator<? super T, ?>> translators;
private Builder(EntityFactory<T> factory) {
this.factory = factory;
translators = new ObjectArrayList<>();
}
public Builder(EntityFactory<T> factory, EntityType type, int bedrockId, String identifier, float width, float height, float offset, List<EntityMetadataTranslator<? super T, ?>> 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<T> heightAndWidth(float value) {
height = value;
width = value;
return this;
}
/**
* Resets the identifier as well
*/
public Builder<T> type(EntityType type) {
this.type = type;
identifier = null;
return this;
}
public <U> Builder<T> addTranslator(MetadataType type, BiConsumer<T, EntityMetadata<U>> translateFunction) {
translators.add(new EntityMetadataTranslator<>(type, translateFunction));
return this;
}
public Builder<T> addTranslator(EntityMetadataTranslator<T, ?> translator) {
translators.add(translator);
return this;
}
public EntityDefinition<T> 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<T> build(boolean register) {
if (identifier == null && type != null) {
identifier = "minecraft:" + type.name().toLowerCase(Locale.ROOT);
}
EntityDefinition<T> 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;
}
}
}

View File

@ -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<AreaEffectCloudEntity> AREA_EFFECT_CLOUD;
public static final EntityDefinition<ArmorStandEntity> ARMOR_STAND;
public static final EntityDefinition<TippedArrowEntity> ARROW;
public static final EntityDefinition<AxolotlEntity> AXOLOTL;
public static final EntityDefinition<BatEntity> BAT;
public static final EntityDefinition<BeeEntity> BEE;
public static final EntityDefinition<BlazeEntity> BLAZE;
public static final EntityDefinition<BoatEntity> BOAT;
public static final EntityDefinition<CatEntity> CAT;
public static final EntityDefinition<MonsterEntity> CAVE_SPIDER;
public static final EntityDefinition<ChickenEntity> CHICKEN;
public static final EntityDefinition<CreeperEntity> CREEPER;
public static final EntityDefinition<AbstractFishEntity> COD;
public static final EntityDefinition<AnimalEntity> COW;
public static final EntityDefinition<ChestedHorseEntity> DONKEY;
public static final EntityDefinition<WaterEntity> DOLPHIN;
public static final EntityDefinition<ItemedFireballEntity> DRAGON_FIREBALL;
public static final EntityDefinition<ZombieEntity> DROWNED;
public static final EntityDefinition<ElderGuardianEntity> ELDER_GUARDIAN;
public static final EntityDefinition<EndermanEntity> ENDERMAN;
public static final EntityDefinition<MonsterEntity> ENDERMITE;
public static final EntityDefinition<EnderDragonEntity> ENDER_DRAGON;
public static final EntityDefinition<EnderCrystalEntity> END_CRYSTAL;
public static final EntityDefinition<SpellcasterIllagerEntity> EVOKER;
public static final EntityDefinition<Entity> EVOKER_FANGS;
public static final EntityDefinition<ExpOrbEntity> EXPERIENCE_ORB;
public static final EntityDefinition<Entity> EYE_OF_ENDER;
public static final EntityDefinition<FallingBlockEntity> FALLING_BLOCK;
public static final EntityDefinition<ItemedFireballEntity> FIREBALL;
public static final EntityDefinition<FishingHookEntity> FISHING_BOBBER;
public static final EntityDefinition<FireworkEntity> FIREWORK_ROCKET;
public static final EntityDefinition<FoxEntity> FOX;
public static final EntityDefinition<GiantEntity> GIANT;
public static final EntityDefinition<GhastEntity> GHAST;
public static final EntityDefinition<ItemFrameEntity> GLOW_ITEM_FRAME;
public static final EntityDefinition<GlowSquidEntity> GLOW_SQUID;
public static final EntityDefinition<GoatEntity> GOAT;
public static final EntityDefinition<GuardianEntity> GUARDIAN;
public static final EntityDefinition<HoglinEntity> HOGLIN;
public static final EntityDefinition<HorseEntity> HORSE;
public static final EntityDefinition<ZombieEntity> HUSK;
public static final EntityDefinition<SpellcasterIllagerEntity> ILLUSIONER; // Not present on Bedrock
public static final EntityDefinition<IronGolemEntity> IRON_GOLEM;
public static final EntityDefinition<ItemEntity> ITEM;
public static final EntityDefinition<ItemFrameEntity> ITEM_FRAME;
public static final EntityDefinition<LeashKnotEntity> LEASH_KNOT;
public static final EntityDefinition<LightningEntity> LIGHTNING_BOLT;
public static final EntityDefinition<LlamaEntity> LLAMA;
public static final EntityDefinition<ThrowableEntity> LLAMA_SPIT;
public static final EntityDefinition<MagmaCubeEntity> MAGMA_CUBE;
public static final EntityDefinition<MinecartEntity> MINECART;
public static final EntityDefinition<MinecartEntity> MINECART_CHEST;
public static final EntityDefinition<CommandBlockMinecartEntity> MINECART_COMMAND_BLOCK;
public static final EntityDefinition<MinecartEntity> MINECART_HOPPER;
public static final EntityDefinition<FurnaceMinecartEntity> MINECART_FURNACE; // Not present on Bedrock
public static final EntityDefinition<SpawnerMinecartEntity> MINECART_SPAWNER; // Not present on Bedrock
public static final EntityDefinition<MinecartEntity> MINECART_TNT;
public static final EntityDefinition<MooshroomEntity> MOOSHROOM;
public static final EntityDefinition<ChestedHorseEntity> MULE;
public static final EntityDefinition<OcelotEntity> OCELOT;
public static final EntityDefinition<PaintingEntity> PAINTING;
public static final EntityDefinition<PandaEntity> PANDA;
public static final EntityDefinition<ParrotEntity> PARROT;
public static final EntityDefinition<PhantomEntity> PHANTOM;
public static final EntityDefinition<PigEntity> PIG;
public static final EntityDefinition<PiglinEntity> PIGLIN;
public static final EntityDefinition<BasePiglinEntity> PIGLIN_BRUTE;
public static final EntityDefinition<PillagerEntity> PILLAGER;
public static final EntityDefinition<PlayerEntity> PLAYER;
public static final EntityDefinition<PolarBearEntity> POLAR_BEAR;
public static final EntityDefinition<TNTEntity> PRIMED_TNT;
public static final EntityDefinition<PufferFishEntity> PUFFERFISH;
public static final EntityDefinition<RabbitEntity> RABBIT;
public static final EntityDefinition<RaidParticipantEntity> RAVAGER;
public static final EntityDefinition<AbstractFishEntity> SALMON;
public static final EntityDefinition<SheepEntity> SHEEP;
public static final EntityDefinition<ShulkerEntity> SHULKER;
public static final EntityDefinition<ThrowableEntity> SHULKER_BULLET;
public static final EntityDefinition<MonsterEntity> SILVERFISH;
public static final EntityDefinition<SkeletonEntity> SKELETON;
public static final EntityDefinition<AbstractHorseEntity> SKELETON_HORSE;
public static final EntityDefinition<SlimeEntity> SLIME;
public static final EntityDefinition<ItemedFireballEntity> SMALL_FIREBALL;
public static final EntityDefinition<ThrowableItemEntity> SNOWBALL;
public static final EntityDefinition<SnowGolemEntity> SNOW_GOLEM;
public static final EntityDefinition<AbstractArrowEntity> SPECTRAL_ARROW;
public static final EntityDefinition<SpiderEntity> SPIDER;
public static final EntityDefinition<SquidEntity> SQUID;
public static final EntityDefinition<AbstractSkeletonEntity> STRAY;
public static final EntityDefinition<StriderEntity> STRIDER;
public static final EntityDefinition<ThrowableItemEntity> THROWN_EGG;
public static final EntityDefinition<ThrowableItemEntity> THROWN_ENDERPEARL;
public static final EntityDefinition<ThrowableItemEntity> THROWN_EXP_BOTTLE;
public static final EntityDefinition<ThrownPotionEntity> THROWN_POTION;
public static final EntityDefinition<TropicalFishEntity> TROPICAL_FISH;
public static final EntityDefinition<TurtleEntity> TURTLE;
public static final EntityDefinition<TraderLlamaEntity> TRADER_LLAMA;
public static final EntityDefinition<TridentEntity> TRIDENT;
public static final EntityDefinition<AbstractMerchantEntity> WANDERING_TRADER;
public static final EntityDefinition<RaidParticipantEntity> WITCH;
public static final EntityDefinition<WitherEntity> WITHER;
public static final EntityDefinition<AbstractSkeletonEntity> WITHER_SKELETON;
public static final EntityDefinition<WitherSkullEntity> WITHER_SKULL;
public static final EntityDefinition<WolfEntity> WOLF;
public static final EntityDefinition<VillagerEntity> VILLAGER;
public static final EntityDefinition<VindicatorEntity> VINDICATOR;
public static final EntityDefinition<VexEntity> VEX;
public static final EntityDefinition<ZoglinEntity> ZOGLIN;
public static final EntityDefinition<ZombieEntity> ZOMBIE;
public static final EntityDefinition<AbstractHorseEntity> ZOMBIE_HORSE;
public static final EntityDefinition<ZombieVillagerEntity> ZOMBIE_VILLAGER;
public static final EntityDefinition<ZombifiedPiglinEntity> ZOMBIFIED_PIGLIN;
/**
* Is not sent over the network
*/
public static final EntityDefinition<EnderDragonPartEntity> ENDER_DRAGON_PART;
/**
* Special Bedrock type
*/
public static final EntityDefinition<WitherSkullEntity> WITHER_SKULL_DANGEROUS;
static {
EntityDefinition<Entity> entityBase = EntityDefinition.builder((BaseEntityFactory<Entity>) 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)
.<Boolean>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
.<Float>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)
.<Boolean>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<FallingBlockEntity>() {
}, 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<FishingHookEntity>() {
}, 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<AbstractArrowEntity> 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
.<Boolean>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<ItemFrameEntity>() {
}, 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
.<Float>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()))
.<Component>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<LivingEntity> livingEntityBase = EntityDefinition.inherited(LivingEntity::new, entityBase)
.addTranslator(MetadataType.BYTE, LivingEntity::setLivingEntityFlags)
.addTranslator(MetadataType.FLOAT, LivingEntity::setHealth)
.<Float>addTranslator(MetadataType.FLOAT,
(livingEntity, entityMetadata) -> livingEntity.getDirtyMetadata().put(EntityData.EFFECT_COLOR, entityMetadata.getValue()))
.<Boolean>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.<PlayerEntity>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<MobEntity> 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)
.<Boolean>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<AbstractFishEntity> 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<BasePiglinEntity> 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<RaidParticipantEntity> raidParticipantEntityBase = EntityDefinition.inherited(RaidParticipantEntity::new, mobEntityBase)
.addTranslator(null) // Celebrating //TODO
.build();
EntityDefinition<SpellcasterIllagerEntity> 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<AgeableEntity> 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)
.<String>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)
.<Boolean>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)
.<Boolean>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)
.<Boolean>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<AbstractMerchantEntity> 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<AbstractHorseEntity> 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<ChestedHorseEntity> chestedHorseEntityBase = EntityDefinition.inherited(ChestedHorseEntity::new, abstractHorseEntityBase)
.<Boolean>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)
.<Integer>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<TameableEntity> 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
.<Boolean>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.<EnderDragonPartEntity>builder(null)
.bedrockId(32)
.identifier("minecraft:armor_stand") // Emulated
.build();
}
public static void init() {
// no-op
}
private EntityDefinitions() {
}
}

View File

@ -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<E extends Entity, T>(
MetadataType acceptedType,
BiConsumer<E, EntityMetadata<T>> translateFunction) {
}

View File

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

View File

@ -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<Boolean> 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());
}
}

View File

@ -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<ItemStack> 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<NbtMap> 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<NbtMap> 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<OptionalInt> 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);
}
}
}

View File

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

View File

@ -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<Boolean> 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);
}
}

View File

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

View File

@ -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<ItemStack> 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<Integer> 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;
}
/**

View File

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

View File

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

View File

@ -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();

View File

@ -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<Byte> 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<Float> 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<Position> 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<Integer> entityMetadata) {
float freezingPercentage = super.setFreezing(entityMetadata);
this.isMaxFrozenState = freezingPercentage >= 1.0f;
setFlag(EntityFlag.SHAKING, isShaking());
return freezingPercentage;
}
/**

View File

@ -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<Integer> entityMetadata) {
dirtyMetadata.put(EntityData.DISPLAY_ITEM, session.getBlockMappings().getBedrockBlockId(((IntEntityMetadata) entityMetadata).getPrimitiveValue()));
}
public void setCustomBlockOffset(EntityMetadata<Integer> entityMetadata) {
dirtyMetadata.put(EntityData.DISPLAY_OFFSET, entityMetadata.getValue());
}
public void setShowCustomBlock(EntityMetadata<Boolean> 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);
}
}

View File

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

View File

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

View File

@ -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<Integer> 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);

View File

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

View File

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

View File

@ -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<Potion> 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<ItemStack> 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);
}
}
}

View File

@ -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();
}

View File

@ -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<Integer> 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);
}
}

View File

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

View File

@ -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<Boolean> 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();
}
}

View File

@ -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<T extends Entity> extends EntityFactory<T> {
T create(GeyserSession session, long javaId, long bedrockId, UUID uuid, EntityDefinition<?> definition, Vector3f position, Vector3f motion, float yaw, float pitch, float headYaw);
}

View File

@ -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<T extends Entity> {
}

View File

@ -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> {
ExpOrbEntity create(GeyserSession session, int amount, long entityId, long geyserId, Vector3f position);
}

View File

@ -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> {
PaintingEntity create(GeyserSession session, long entityId, long geyserId, UUID uuid, Vector3f position, PaintingType paintingName, int direction);
}

View File

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

View File

@ -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<Boolean> 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);
}
}

View File

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

View File

@ -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<Component> entityMetadata) {
super.setDisplayName(entityMetadata);
updateSecondEntityStatus(false);
}
public void setArmorStandFlags(EntityMetadata<Byte> 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<Rotation> entityMetadata) {
onRotationUpdate(EntityData.MARK_VARIANT, EntityFlag.INTERESTED, EntityFlag.CHARGED, EntityFlag.POWERED, entityMetadata.getValue());
}
public void setBodyRotation(EntityMetadata<Rotation> entityMetadata) {
onRotationUpdate(EntityData.VARIANT, EntityFlag.IN_LOVE, EntityFlag.CELEBRATING, EntityFlag.CELEBRATING_SPECIAL, entityMetadata.getValue());
}
public void setLeftArmRotation(EntityMetadata<Rotation> entityMetadata) {
onRotationUpdate(EntityData.TRADE_TIER, EntityFlag.CHARGING, EntityFlag.CRITICAL, EntityFlag.DANCING, entityMetadata.getValue());
}
public void setRightArmRotation(EntityMetadata<Rotation> entityMetadata) {
onRotationUpdate(EntityData.MAX_TRADE_TIER, EntityFlag.ELDER, EntityFlag.EMOTING, EntityFlag.IDLING, entityMetadata.getValue());
}
public void setLeftLegRotation(EntityMetadata<Rotation> entityMetadata) {
onRotationUpdate(EntityData.SKIN_ID, EntityFlag.IS_ILLAGER_CAPTAIN, EntityFlag.IS_IN_UI, EntityFlag.LINGERING, entityMetadata.getValue());
}
public void setRightLegRotation(EntityMetadata<Rotation> 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);

View File

@ -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<Byte> entityMetadata) {
byte xd = ((ByteEntityMetadata) entityMetadata).getPrimitiveValue();
setFlag(EntityFlag.RESTING, (xd & 0x01) == 0x01);
}
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -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();
}
}
}

View File

@ -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<Byte> 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);
}
}

View File

@ -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<Integer> entityMetadata) {
dirtyMetadata.put(EntityData.SCALE, 0.10f + ((IntEntityMetadata) entityMetadata).getPrimitiveValue());
}
}

View File

@ -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<Byte> entityMetadata) {
byte xd = ((ByteEntityMetadata) entityMetadata).getPrimitiveValue();
// Handle the visibility of the pumpkin
setFlag(EntityFlag.SHEARED, (xd & 0x10) != 0x10);
}
}

View File

@ -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());

View File

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

View File

@ -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);
}
/**

View File

@ -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<Integer> 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<Boolean> entityMetadata) {
setFlag(EntityFlag.PLAYING_DEAD, ((BooleanEntityMetadata) entityMetadata).getPrimitiveValue());
}
@Override

View File

@ -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<Byte> 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<Integer> entityMetadata) {
// Converting "anger time" to a boolean
setFlag(EntityFlag.ANGRY, ((IntEntityMetadata) entityMetadata).getPrimitiveValue() > 0);
}
@Override

View File

@ -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

View File

@ -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<Integer> entityMetadata) {
dirtyMetadata.put(EntityData.VARIANT, entityMetadata.getValue());
}
public void setFoxFlags(EntityMetadata<Byte> 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

View File

@ -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<Boolean> 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);
}

View File

@ -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<Boolean> 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

View File

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

View File

@ -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

View File

@ -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<Integer> 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<Byte> entityMetadata) {
mainGene = ((ByteEntityMetadata) entityMetadata).getPrimitiveValue();
updateAppearance();
}
public void setHiddenGene(EntityMetadata<Byte> entityMetadata) {
hiddenGene = ((ByteEntityMetadata) entityMetadata).getPrimitiveValue();
updateAppearance();
}
public void setPandaFlags(EntityMetadata<Byte> 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);
}
}
}

View File

@ -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

View File

@ -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

View File

@ -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<Integer> entityMetadata) {
int puffsize = ((IntEntityMetadata) entityMetadata).getPrimitiveValue();
dirtyMetadata.put(EntityData.PUFFERFISH_SIZE, (byte) puffsize);
dirtyMetadata.put(EntityData.VARIANT, puffsize);
}
}

View File

@ -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<Boolean> 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<Integer> 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

View File

@ -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<Byte> entityMetadata) {
byte xd = ((ByteEntityMetadata) entityMetadata).getPrimitiveValue();
setFlag(EntityFlag.SHEARED, (xd & 0x10) == 0x10);
dirtyMetadata.put(EntityData.COLOR, xd);
}
}

View File

@ -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<Boolean> entityMetadata) {
isCold = ((BooleanEntityMetadata) entityMetadata).getPrimitiveValue();
}
public void setSaddled(EntityMetadata<Boolean> 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

View File

@ -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<String> VARIANT_NAMES = ImmutableList.of("kob", "sunstreak", "snooper", "dasher", "brinely", "spotty", "flopper", "stripey", "glitter", "blockfish", "betty", "clayfish");
private static final List<String> 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<Integer> 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) {

View File

@ -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<Boolean> entityMetadata) {
setFlag(EntityFlag.IS_PREGNANT, ((BooleanEntityMetadata) entityMetadata).getPrimitiveValue());
}
public void setLayingEgg(EntityMetadata<Boolean> entityMetadata) {
setFlag(EntityFlag.LAYING_EGG, ((BooleanEntityMetadata) entityMetadata).getPrimitiveValue());
}
@Override

View File

@ -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<String> 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<Byte> 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

View File

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

View File

@ -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<Integer> entityMetadata) {
int value = ((IntEntityMetadata) entityMetadata).getPrimitiveValue();
dirtyMetadata.put(EntityData.VARIANT, value & 255);
dirtyMetadata.put(EntityData.MARK_VARIANT, (value >> 8) % 5);
}
}

View File

@ -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<Integer> 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

View File

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

View File

@ -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<Byte> 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<Integer> 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<Boolean> entityMetadata) {
setFlag(EntityFlag.RESTING, ((BooleanEntityMetadata) entityMetadata).getPrimitiveValue());
}
public void setCollarColor(EntityMetadata<Integer> entityMetadata) {
collarColor = (byte) ((IntEntityMetadata) entityMetadata).getPrimitiveValue();
// Needed or else wild cats are a red color
if (getFlag(EntityFlag.TAMED)) {
dirtyMetadata.put(EntityData.COLOR, collarColor);
}
}

View File

@ -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

View File

@ -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<Byte> 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<UUID> 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);
}
}

View File

@ -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<Byte> 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<Integer> 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<Integer> 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);
}
}

View File

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

View File

@ -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<VillagerData> 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<Position> 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);

View File

@ -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<Byte> 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);
}
}

View File

@ -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<Boolean> 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();
}
}

View File

@ -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<Byte> entityMetadata) {
byte xd = ((ByteEntityMetadata) entityMetadata).getPrimitiveValue();
setFlag(EntityFlag.ON_FIRE, (xd & 0x01) == 0x01);
}
}

View File

@ -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<Integer> entityMetadata) {
if (!ignitedByFlintAndSteel) {
setFlag(EntityFlag.IGNITED, ((IntEntityMetadata) entityMetadata).getPrimitiveValue() == 1);
}
}
super.updateBedrockMetadata(entityMetadata, session);
public void setIgnited(EntityMetadata<Boolean> entityMetadata) {
ignitedByFlintAndSteel = ((BooleanEntityMetadata) entityMetadata).getPrimitiveValue();
setFlag(EntityFlag.IGNITED, ignitedByFlintAndSteel);
}
}

View File

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

View File

@ -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<Float> 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<Integer> 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();
}

View File

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

View File

@ -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<Integer> entityMetadata) {
dirtyMetadata.put(EntityData.CARRIED_BLOCK, session.getBlockMappings().getBedrockBlockId(((IntEntityMetadata) entityMetadata).getPrimitiveValue()));
}
/**
* Controls the screaming sound
*/
public void setScreaming(EntityMetadata<Boolean> 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<Boolean> 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());
}
}

View File

@ -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<Boolean> entityMetadata) {
// If the ghast is attacking
dirtyMetadata.put(EntityData.CHARGE_AMOUNT, (byte) (((BooleanEntityMetadata) entityMetadata).getPrimitiveValue() ? 1 : 0));
}
}

View File

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

View File

@ -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<Integer> 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);
}
}
}

View File

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

View File

@ -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<Integer> 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);
}
}

View File

@ -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<Boolean> 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<Boolean> entityMetadata) {
setFlag(EntityFlag.CHARGING, ((BooleanEntityMetadata) entityMetadata).getPrimitiveValue());
}
public void setDancing(EntityMetadata<Boolean> 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);
}

View File

@ -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<Direction> 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<Byte> 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<Byte> 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);
}
}

View File

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

View File

@ -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<Byte> entityMetadata) {
byte xd = ((ByteEntityMetadata) entityMetadata).getPrimitiveValue();
setFlag(EntityFlag.WALL_CLIMBING, (xd & 0x01) == 0x01);
}
}

Some files were not shown because too many files have changed in this diff Show More