From b7828267a513c1f030327494a90265613f9dacfc Mon Sep 17 00:00:00 2001 From: David Choo Date: Fri, 19 Feb 2021 11:12:36 -0500 Subject: [PATCH] Projectile Movement (#1929) Fixes #1780 #1778 The laggy fireballs Llama spit Shulker bullets --- .../connector/entity/AbstractArrowEntity.java | 18 ++ .../connector/entity/FishingHookEntity.java | 158 ++++++++++++++++-- .../entity/ItemedFireballEntity.java | 41 ++++- .../connector/entity/ThrowableEntity.java | 79 +++++++-- .../connector/entity/WitherSkullEntity.java | 2 + .../connector/entity/type/EntityType.java | 4 +- .../collision/CollisionManager.java | 24 +-- .../spawn/JavaSpawnEntityTranslator.java | 15 +- .../world/block/BlockStateValues.java | 65 ++++--- .../world/block/BlockTranslator.java | 2 +- connector/src/main/resources/mappings | 2 +- 11 files changed, 332 insertions(+), 78 deletions(-) diff --git a/connector/src/main/java/org/geysermc/connector/entity/AbstractArrowEntity.java b/connector/src/main/java/org/geysermc/connector/entity/AbstractArrowEntity.java index e9a4a1f98..70dbdf959 100644 --- a/connector/src/main/java/org/geysermc/connector/entity/AbstractArrowEntity.java +++ b/connector/src/main/java/org/geysermc/connector/entity/AbstractArrowEntity.java @@ -35,6 +35,8 @@ 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); + + setMotion(motion); } @Override @@ -47,4 +49,20 @@ public class AbstractArrowEntity extends Entity { super.updateBedrockMetadata(entityMetadata, session); } + + @Override + public void setRotation(Vector3f rotation) { + // Ignore the rotation sent by the Java server since the + // Java client calculates the rotation from the motion + } + + @Override + public void setMotion(Vector3f motion) { + 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); + } } diff --git a/connector/src/main/java/org/geysermc/connector/entity/FishingHookEntity.java b/connector/src/main/java/org/geysermc/connector/entity/FishingHookEntity.java index 9f9b7bc38..06e56ad03 100644 --- a/connector/src/main/java/org/geysermc/connector/entity/FishingHookEntity.java +++ b/connector/src/main/java/org/geysermc/connector/entity/FishingHookEntity.java @@ -26,43 +26,167 @@ 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.object.ProjectileData; import com.nukkitx.math.vector.Vector3f; +import com.nukkitx.math.vector.Vector3i; import com.nukkitx.protocol.bedrock.data.entity.EntityData; -import org.geysermc.connector.GeyserConnector; +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.CollisionManager; +import org.geysermc.connector.network.translators.collision.CollisionTranslator; +import org.geysermc.connector.network.translators.collision.translators.BlockCollision; +import org.geysermc.connector.network.translators.world.block.BlockStateValues; +import org.geysermc.connector.network.translators.world.block.BlockTranslator; -public class FishingHookEntity extends Entity { - public FishingHookEntity(long entityId, long geyserId, EntityType entityType, Vector3f position, Vector3f motion, Vector3f rotation, ProjectileData data) { +import java.util.List; +import java.util.concurrent.ThreadLocalRandom; + +public class FishingHookEntity extends ThrowableEntity { + + private boolean hooked = false; + + private final BoundingBox boundingBox; + + 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); - for (GeyserSession session : GeyserConnector.getInstance().getPlayers()) { - Entity entity = session.getEntityCache().getEntityByJavaId(data.getOwnerId()); - if (entity == null && session.getPlayerEntity().getEntityId() == data.getOwnerId()) { - entity = session.getPlayerEntity(); - } + this.boundingBox = new BoundingBox(0.125, 0.125, 0.125, 0.25, 0.25, 0.25); - if (entity != null) { - this.metadata.put(EntityData.OWNER_EID, entity.getGeyserId()); - return; - } - } + // 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.metadata.put(EntityData.OWNER_EID, owner.getGeyserId()); } @Override public void updateBedrockMetadata(EntityMetadata entityMetadata, GeyserSession session) { - if (entityMetadata.getId() == 7) { - Entity entity = session.getEntityCache().getEntityByJavaId((Integer) entityMetadata.getValue() - 1); - if (entity == null && session.getPlayerEntity().getEntityId() == (Integer) entityMetadata.getValue() - 1) { + if (entityMetadata.getId() == 7) { // Hooked entity + int hookedEntityId = (int) entityMetadata.getValue() - 1; + Entity entity = session.getEntityCache().getEntityByJavaId(hookedEntityId); + if (entity == null && session.getPlayerEntity().getEntityId() == hookedEntityId) { entity = session.getPlayerEntity(); } if (entity != null) { metadata.put(EntityData.TARGET_EID, entity.getGeyserId()); + hooked = true; + } else { + hooked = false; } } super.updateBedrockMetadata(entityMetadata, session); } + + @Override + protected void moveAbsoluteImmediate(GeyserSession session, Vector3f position, Vector3f rotation, boolean isOnGround, boolean teleported) { + boundingBox.setMiddleX(position.getX()); + boundingBox.setMiddleY(position.getY() + boundingBox.getSizeY() / 2); + boundingBox.setMiddleZ(position.getZ()); + + CollisionManager collisionManager = session.getCollisionManager(); + List collidableBlocks = collisionManager.getCollidableBlocks(boundingBox); + boolean touchingWater = false; + boolean collided = false; + for (Vector3i blockPos : collidableBlocks) { + if (0 <= blockPos.getY() && blockPos.getY() <= 255) { + int blockID = session.getConnector().getWorldManager().getBlockAt(session, blockPos); + BlockCollision blockCollision = CollisionTranslator.getCollision(blockID, blockPos.getX(), blockPos.getY(), blockPos.getZ()); + if (blockCollision != null && blockCollision.checkIntersection(boundingBox)) { + // TODO Push bounding box out of collision to improve movement + collided = true; + } + + int waterLevel = BlockStateValues.getWaterLevel(blockID); + if (BlockTranslator.isWaterlogged(blockID)) { + waterLevel = 0; + } + if (waterLevel >= 0) { + double waterMaxY = blockPos.getY() + 1 - (waterLevel + 1) / 9.0; + // Falling water is a full block + if (waterLevel >= 8) { + waterMaxY = blockPos.getY() + 1; + } + if (position.getY() <= waterMaxY) { + touchingWater = true; + } + } + } + } + + if (!inWater && touchingWater) { + sendSplashSound(session); + } + inWater = touchingWater; + + if (!collided) { + super.moveAbsoluteImmediate(session, position, rotation, isOnGround, teleported); + } else { + super.moveAbsoluteImmediate(session, this.position, rotation, true, true); + } + } + + private void sendSplashSound(GeyserSession session) { + if (!metadata.getFlags().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; + } + PlaySoundPacket playSoundPacket = new PlaySoundPacket(); + playSoundPacket.setSound("random.splash"); + playSoundPacket.setPosition(position); + playSoundPacket.setVolume(volume); + playSoundPacket.setPitch(1f + ThreadLocalRandom.current().nextFloat() * 0.3f); + session.sendUpstreamPacket(playSoundPacket); + } + } + + @Override + public void tick(GeyserSession session) { + if (hooked || !isInAir(session) && !isInWater(session) || isOnGround()) { + motion = Vector3f.ZERO; + return; + } + float gravity = getGravity(session); + motion = motion.down(gravity); + + moveAbsoluteImmediate(session, position.add(motion), rotation, onGround, false); + + float drag = getDrag(session); + motion = motion.mul(drag); + } + + @Override + protected float getGravity(GeyserSession session) { + if (!isInWater(session) && !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) { + if (session.getConnector().getConfig().isCacheChunks()) { + if (0 <= position.getFloorY() && position.getFloorY() <= 255) { + int block = session.getConnector().getWorldManager().getBlockAt(session, position.toInt()); + return block == BlockTranslator.JAVA_AIR_ID; + } + } + return false; + } + + @Override + protected float getDrag(GeyserSession session) { + return 0.92f; + } } diff --git a/connector/src/main/java/org/geysermc/connector/entity/ItemedFireballEntity.java b/connector/src/main/java/org/geysermc/connector/entity/ItemedFireballEntity.java index 2b411109a..58a3b6f6c 100644 --- a/connector/src/main/java/org/geysermc/connector/entity/ItemedFireballEntity.java +++ b/connector/src/main/java/org/geysermc/connector/entity/ItemedFireballEntity.java @@ -32,19 +32,44 @@ import org.geysermc.connector.network.session.GeyserSession; public class ItemedFireballEntity extends ThrowableEntity { private final Vector3f acceleration; + /** + * The number of ticks to advance movement before sending to Bedrock + */ + 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); - acceleration = motion; + + float magnitude = motion.length(); + if (magnitude != 0) { + acceleration = motion.div(magnitude).mul(0.1f); + } else { + acceleration = Vector3f.ZERO; + } + } + + private Vector3f tickMovement(GeyserSession session, Vector3f position) { + position = position.add(motion); + float drag = getDrag(session); + motion = motion.add(acceleration).mul(drag); + return position; + } + + @Override + protected void moveAbsoluteImmediate(GeyserSession session, Vector3f position, Vector3f rotation, 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); + } + super.moveAbsoluteImmediate(session, newPosition, rotation, isOnGround, teleported); + this.position = position; + this.motion = lastMotion; } @Override public void tick(GeyserSession session) { - position = position.add(motion); - // TODO: While this reduces latency in position updating (needed for better fireball reflecting), - // TODO: movement is incredibly stiff. - // TODO: Only use this laggy movement for fireballs that be reflected - moveAbsoluteImmediate(session, position, rotation, false, true); - float drag = getDrag(session); - motion = motion.add(acceleration).mul(drag); + moveAbsoluteImmediate(session, tickMovement(session, position), rotation, false, false); } } diff --git a/connector/src/main/java/org/geysermc/connector/entity/ThrowableEntity.java b/connector/src/main/java/org/geysermc/connector/entity/ThrowableEntity.java index 4e0c25ab5..1088b2a0b 100644 --- a/connector/src/main/java/org/geysermc/connector/entity/ThrowableEntity.java +++ b/connector/src/main/java/org/geysermc/connector/entity/ThrowableEntity.java @@ -29,20 +29,21 @@ 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.BlockTranslator; +import org.geysermc.connector.network.translators.world.block.BlockStateValues; /** * Used as a class for any object-like entity that moves as a projectile */ public class ThrowableEntity extends Entity implements Tickable { - private Vector3f lastPosition; + protected Vector3f lastJavaPosition; public ThrowableEntity(long entityId, long geyserId, EntityType entityType, Vector3f position, Vector3f motion, Vector3f rotation) { super(entityId, geyserId, entityType, position, motion, rotation); - this.lastPosition = position; + this.lastJavaPosition = position; } /** @@ -52,22 +53,65 @@ public class ThrowableEntity extends Entity implements Tickable { */ @Override public void tick(GeyserSession session) { - super.moveRelative(session, motion.getX(), motion.getY(), motion.getZ(), rotation, onGround); + moveAbsoluteImmediate(session, position.add(motion), rotation, onGround, false); float drag = getDrag(session); - float gravity = getGravity(); + float gravity = getGravity(session); motion = motion.mul(drag).down(gravity); } protected void moveAbsoluteImmediate(GeyserSession session, Vector3f position, Vector3f rotation, boolean isOnGround, boolean teleported) { - super.moveAbsolute(session, position, rotation, isOnGround, teleported); + MoveEntityDeltaPacket moveEntityDeltaPacket = new MoveEntityDeltaPacket(); + moveEntityDeltaPacket.setRuntimeEntityId(geyserId); + + if (isOnGround) { + moveEntityDeltaPacket.getFlags().add(MoveEntityDeltaPacket.Flag.ON_GROUND); + } + setOnGround(isOnGround); + + if (teleported) { + moveEntityDeltaPacket.getFlags().add(MoveEntityDeltaPacket.Flag.TELEPORTING); + } + + if (this.position.getX() != position.getX()) { + moveEntityDeltaPacket.getFlags().add(MoveEntityDeltaPacket.Flag.HAS_X); + moveEntityDeltaPacket.setX(position.getX()); + } + if (this.position.getY() != position.getY()) { + moveEntityDeltaPacket.getFlags().add(MoveEntityDeltaPacket.Flag.HAS_Y); + moveEntityDeltaPacket.setY(position.getY()); + } + if (this.position.getZ() != position.getZ()) { + moveEntityDeltaPacket.getFlags().add(MoveEntityDeltaPacket.Flag.HAS_Z); + moveEntityDeltaPacket.setZ(position.getZ()); + } + setPosition(position); + + if (this.rotation.getX() != rotation.getX()) { + moveEntityDeltaPacket.getFlags().add(MoveEntityDeltaPacket.Flag.HAS_YAW); + moveEntityDeltaPacket.setYaw(rotation.getX()); + } + if (this.rotation.getY() != rotation.getY()) { + moveEntityDeltaPacket.getFlags().add(MoveEntityDeltaPacket.Flag.HAS_PITCH); + moveEntityDeltaPacket.setPitch(rotation.getY()); + } + if (this.rotation.getZ() != rotation.getZ()) { + moveEntityDeltaPacket.getFlags().add(MoveEntityDeltaPacket.Flag.HAS_HEAD_YAW); + moveEntityDeltaPacket.setHeadYaw(rotation.getZ()); + } + setRotation(rotation); + + if (!moveEntityDeltaPacket.getFlags().isEmpty()) { + session.sendUpstreamPacket(moveEntityDeltaPacket); + } } /** * 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() { + protected float getGravity(GeyserSession session) { if (metadata.getFlags().getFlag(EntityFlag.HAS_GRAVITY)) { switch (entityType) { case THROWN_POTION: @@ -76,11 +120,14 @@ public class ThrowableEntity extends Entity implements Tickable { case THROWN_EXP_BOTTLE: return 0.07f; case FIREBALL: + case SHULKER_BULLET: return 0; case SNOWBALL: case THROWN_EGG: case THROWN_ENDERPEARL: return 0.03f; + case LLAMA_SPIT: + return 0.06f; } } return 0; @@ -101,11 +148,14 @@ public class ThrowableEntity extends Entity implements Tickable { case SNOWBALL: case THROWN_EGG: case THROWN_ENDERPEARL: + case LLAMA_SPIT: return 0.99f; case FIREBALL: case SMALL_FIREBALL: case DRAGON_FIREBALL: return 0.95f; + case SHULKER_BULLET: + return 1; } } return 1; @@ -117,8 +167,10 @@ public class ThrowableEntity extends Entity implements Tickable { */ protected boolean isInWater(GeyserSession session) { if (session.getConnector().getConfig().isCacheChunks()) { - int block = session.getConnector().getWorldManager().getBlockAt(session, position.toInt()); - return block == BlockTranslator.BEDROCK_WATER_ID; + if (0 <= position.getFloorY() && position.getFloorY() <= 255) { + int block = session.getConnector().getWorldManager().getBlockAt(session, position.toInt()); + return BlockStateValues.getWaterLevel(block) != -1; + } } return false; } @@ -136,14 +188,13 @@ public class ThrowableEntity extends Entity implements Tickable { @Override public void moveRelative(GeyserSession session, double relX, double relY, double relZ, Vector3f rotation, boolean isOnGround) { - position = lastPosition; - super.moveRelative(session, relX, relY, relZ, rotation, isOnGround); - lastPosition = position; + moveAbsoluteImmediate(session, lastJavaPosition.add(relX, relY, relZ), rotation, isOnGround, false); + lastJavaPosition = position; } @Override public void moveAbsolute(GeyserSession session, Vector3f position, Vector3f rotation, boolean isOnGround, boolean teleported) { - super.moveAbsolute(session, position, rotation, isOnGround, teleported); - lastPosition = position; + moveAbsoluteImmediate(session, position, rotation, isOnGround, teleported); + lastJavaPosition = position; } } diff --git a/connector/src/main/java/org/geysermc/connector/entity/WitherSkullEntity.java b/connector/src/main/java/org/geysermc/connector/entity/WitherSkullEntity.java index ba5b9eb55..3548e0dfe 100644 --- a/connector/src/main/java/org/geysermc/connector/entity/WitherSkullEntity.java +++ b/connector/src/main/java/org/geysermc/connector/entity/WitherSkullEntity.java @@ -35,6 +35,8 @@ public class WitherSkullEntity extends ItemedFireballEntity { public WitherSkullEntity(long entityId, long geyserId, EntityType entityType, Vector3f position, Vector3f motion, Vector3f rotation) { super(entityId, geyserId, entityType, position, motion, rotation); + + this.futureTicks = 1; } @Override diff --git a/connector/src/main/java/org/geysermc/connector/entity/type/EntityType.java b/connector/src/main/java/org/geysermc/connector/entity/type/EntityType.java index c45a38ad0..f38e56fd8 100644 --- a/connector/src/main/java/org/geysermc/connector/entity/type/EntityType.java +++ b/connector/src/main/java/org/geysermc/connector/entity/type/EntityType.java @@ -118,7 +118,7 @@ public enum EntityType { TRIDENT(TridentEntity.class, 73, 0f, 0f, 0f, 0f, "minecraft:thrown_trident"), TURTLE(TurtleEntity.class, 74, 0.4f, 1.2f), CAT(CatEntity.class, 75, 0.35f, 0.3f), - SHULKER_BULLET(Entity.class, 76, 0.3125f), + SHULKER_BULLET(ThrowableEntity.class, 76, 0.3125f), FISHING_BOBBER(FishingHookEntity.class, 77, 0f, 0f, 0f, 0f, "minecraft:fishing_hook"), CHALKBOARD(Entity.class, 78, 0f), DRAGON_FIREBALL(ItemedFireballEntity.class, 79, 1.0f), @@ -145,7 +145,7 @@ public enum EntityType { MINECART_SPAWNER(SpawnerMinecartEntity.class, 98, 0.7f, 0.98f, 0.98f, 0.35f, "minecraft:minecart"), MINECART_COMMAND_BLOCK(CommandBlockMinecartEntity.class, 100, 0.7f, 0.98f, 0.98f, 0.35f, "minecraft:command_block_minecart"), LINGERING_POTION(ThrowableEntity.class, 101, 0f), - LLAMA_SPIT(Entity.class, 102, 0.25f), + LLAMA_SPIT(ThrowableEntity.class, 102, 0.25f), EVOKER_FANGS(Entity.class, 103, 0.8f, 0.5f, 0.5f, 0f, "minecraft:evocation_fang"), EVOKER(SpellcasterIllagerEntity.class, 104, 1.95f, 0.6f, 0.6f, 0f, "minecraft:evocation_illager"), VEX(VexEntity.class, 105, 0.8f, 0.4f), diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/collision/CollisionManager.java b/connector/src/main/java/org/geysermc/connector/network/translators/collision/CollisionManager.java index 203e4406f..77392a99a 100644 --- a/connector/src/main/java/org/geysermc/connector/network/translators/collision/CollisionManager.java +++ b/connector/src/main/java/org/geysermc/connector/network/translators/collision/CollisionManager.java @@ -177,24 +177,24 @@ public class CollisionManager { session.sendUpstreamPacket(movePlayerPacket); } - public List getPlayerCollidableBlocks() { + public List getCollidableBlocks(BoundingBox box) { List blocks = new ArrayList<>(); - Vector3d position = Vector3d.from(playerBoundingBox.getMiddleX(), - playerBoundingBox.getMiddleY() - (playerBoundingBox.getSizeY() / 2), - playerBoundingBox.getMiddleZ()); + Vector3d position = Vector3d.from(box.getMiddleX(), + box.getMiddleY() - (box.getSizeY() / 2), + box.getMiddleZ()); - // Loop through all blocks that could collide with the player - int minCollisionX = (int) Math.floor(position.getX() - ((playerBoundingBox.getSizeX() / 2) + COLLISION_TOLERANCE)); - int maxCollisionX = (int) Math.floor(position.getX() + (playerBoundingBox.getSizeX() / 2) + COLLISION_TOLERANCE); + // Loop through all blocks that could collide + int minCollisionX = (int) Math.floor(position.getX() - ((box.getSizeX() / 2) + COLLISION_TOLERANCE)); + int maxCollisionX = (int) Math.floor(position.getX() + (box.getSizeX() / 2) + COLLISION_TOLERANCE); // Y extends 0.5 blocks down because of fence hitboxes int minCollisionY = (int) Math.floor(position.getY() - 0.5); - int maxCollisionY = (int) Math.floor(position.getY() + playerBoundingBox.getSizeY()); + int maxCollisionY = (int) Math.floor(position.getY() + box.getSizeY()); - int minCollisionZ = (int) Math.floor(position.getZ() - ((playerBoundingBox.getSizeZ() / 2) + COLLISION_TOLERANCE)); - int maxCollisionZ = (int) Math.floor(position.getZ() + (playerBoundingBox.getSizeZ() / 2) + COLLISION_TOLERANCE); + int minCollisionZ = (int) Math.floor(position.getZ() - ((box.getSizeZ() / 2) + COLLISION_TOLERANCE)); + int maxCollisionZ = (int) Math.floor(position.getZ() + (box.getSizeZ() / 2) + COLLISION_TOLERANCE); for (int y = minCollisionY; y < maxCollisionY + 1; y++) { for (int x = minCollisionX; x < maxCollisionX + 1; x++) { @@ -207,6 +207,10 @@ public class CollisionManager { return blocks; } + public List getPlayerCollidableBlocks() { + return getCollidableBlocks(playerBoundingBox); + } + /** * Returns false if the movement is invalid, and in this case it shouldn't be sent to the server and should be * cancelled diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/java/entity/spawn/JavaSpawnEntityTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/java/entity/spawn/JavaSpawnEntityTranslator.java index efbdfc5b2..b6a41fa42 100644 --- a/connector/src/main/java/org/geysermc/connector/network/translators/java/entity/spawn/JavaSpawnEntityTranslator.java +++ b/connector/src/main/java/org/geysermc/connector/network/translators/java/entity/spawn/JavaSpawnEntityTranslator.java @@ -32,6 +32,7 @@ import com.github.steveice10.mc.protocol.data.game.entity.type.EntityType; import com.github.steveice10.mc.protocol.packet.ingame.server.entity.spawn.ServerSpawnEntityPacket; import com.nukkitx.math.vector.Vector3f; import org.geysermc.connector.entity.*; +import org.geysermc.connector.entity.player.PlayerEntity; import org.geysermc.connector.network.session.GeyserSession; import org.geysermc.connector.network.translators.PacketTranslator; import org.geysermc.connector.network.translators.Translator; @@ -69,8 +70,18 @@ public class JavaSpawnEntityTranslator extends PacketTranslator entry, int javaBlockState) { - JsonNode bannerColor = entry.getValue().get("banner_color"); + public static void storeBlockStateValues(String javaId, int javaBlockState, JsonNode blockData) { + JsonNode bannerColor = blockData.get("banner_color"); if (bannerColor != null) { BANNER_COLORS.put(javaBlockState, (byte) bannerColor.intValue()); return; // There will never be a banner color and a skull variant } - JsonNode bedColor = entry.getValue().get("bed_color"); + JsonNode bedColor = blockData.get("bed_color"); if (bedColor != null) { BED_COLORS.put(javaBlockState, (byte) bedColor.intValue()); return; } - if (entry.getKey().contains("command_block")) { - COMMAND_BLOCK_VALUES.put(javaBlockState, entry.getKey().contains("conditional=true") ? (byte) 1 : (byte) 0); + if (javaId.contains("command_block")) { + COMMAND_BLOCK_VALUES.put(javaBlockState, javaId.contains("conditional=true") ? (byte) 1 : (byte) 0); return; } - if (entry.getValue().get("double_chest_position") != null) { - boolean isX = (entry.getValue().get("x") != null); - boolean isDirectionPositive = ((entry.getValue().get("x") != null && entry.getValue().get("x").asBoolean()) || - (entry.getValue().get("z") != null && entry.getValue().get("z").asBoolean())); - boolean isLeft = (entry.getValue().get("double_chest_position").asText().contains("left")); + if (blockData.get("double_chest_position") != null) { + boolean isX = (blockData.get("x") != null); + boolean isDirectionPositive = ((blockData.get("x") != null && blockData.get("x").asBoolean()) || + (blockData.get("z") != null && blockData.get("z").asBoolean())); + boolean isLeft = (blockData.get("double_chest_position").asText().contains("left")); DOUBLE_CHEST_VALUES.put(javaBlockState, new DoubleChestValue(isX, isDirectionPositive, isLeft)); return; } - if (entry.getKey().contains("potted_") || entry.getKey().contains("flower_pot")) { - FLOWER_POT_VALUES.put(javaBlockState, entry.getKey().replace("potted_", "")); + if (javaId.contains("potted_") || javaId.contains("flower_pot")) { + FLOWER_POT_VALUES.put(javaBlockState, javaId.replace("potted_", "")); return; } - JsonNode notePitch = entry.getValue().get("note_pitch"); + JsonNode notePitch = blockData.get("note_pitch"); if (notePitch != null) { - NOTEBLOCK_PITCHES.put(javaBlockState, entry.getValue().get("note_pitch").intValue()); + NOTEBLOCK_PITCHES.put(javaBlockState, blockData.get("note_pitch").intValue()); return; } - if (entry.getKey().contains("piston")) { + if (javaId.contains("piston")) { // True if extended, false if not - PISTON_VALUES.put(javaBlockState, entry.getKey().contains("extended=true")); - IS_STICKY_PISTON.put(javaBlockState, entry.getKey().contains("sticky")); + PISTON_VALUES.put(javaBlockState, javaId.contains("extended=true")); + IS_STICKY_PISTON.put(javaBlockState, javaId.contains("sticky")); return; } - JsonNode skullVariation = entry.getValue().get("variation"); + JsonNode skullVariation = blockData.get("variation"); if (skullVariation != null) { SKULL_VARIANTS.put(javaBlockState, (byte) skullVariation.intValue()); } - JsonNode skullRotation = entry.getValue().get("skull_rotation"); + JsonNode skullRotation = blockData.get("skull_rotation"); if (skullRotation != null) { SKULL_ROTATIONS.put(javaBlockState, (byte) skullRotation.intValue()); } - if (entry.getKey().contains("wall_skull") || entry.getKey().contains("wall_head")) { - String direction = entry.getKey().substring(entry.getKey().lastIndexOf("facing=") + 7); + if (javaId.contains("wall_skull") || javaId.contains("wall_head")) { + String direction = javaId.substring(javaId.lastIndexOf("facing=") + 7); int rotation = 0; switch (direction.substring(0, direction.length() - 1)) { case "north": @@ -131,10 +133,16 @@ public class BlockStateValues { SKULL_WALL_DIRECTIONS.put(javaBlockState, rotation); } - JsonNode shulkerDirection = entry.getValue().get("shulker_direction"); + JsonNode shulkerDirection = blockData.get("shulker_direction"); if (shulkerDirection != null) { BlockStateValues.SHULKERBOX_DIRECTIONS.put(javaBlockState, (byte) shulkerDirection.intValue()); } + + if (javaId.startsWith("minecraft:water")) { + String strLevel = javaId.substring(javaId.lastIndexOf("level=") + 6, javaId.length() - 1); + int level = Integer.parseInt(strLevel); + WATER_LEVEL.put(javaBlockState, level); + } } /** @@ -263,4 +271,15 @@ public class BlockStateValues { public static byte getShulkerBoxDirection(int state) { return SHULKERBOX_DIRECTIONS.getOrDefault(state, (byte) -1); } + + /** + * Get the level of water from the block state. + * This is used in FishingHookEntity to create splash sounds when the hook hits the water. + * + * @param state BlockState of the block + * @return The water level or -1 if the block isn't water + */ + public static int getWaterLevel(int state) { + return WATER_LEVEL.getOrDefault(state, -1); + } } diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/world/block/BlockTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/world/block/BlockTranslator.java index 06d724728..1d7c86a53 100644 --- a/connector/src/main/java/org/geysermc/connector/network/translators/world/block/BlockTranslator.java +++ b/connector/src/main/java/org/geysermc/connector/network/translators/world/block/BlockTranslator.java @@ -185,7 +185,7 @@ public class BlockTranslator { JAVA_ID_BLOCK_MAP.put(javaId, javaRuntimeId); - BlockStateValues.storeBlockStateValues(entry, javaRuntimeId); + BlockStateValues.storeBlockStateValues(entry.getKey(), javaRuntimeId, entry.getValue()); String cleanJavaIdentifier = entry.getKey().split("\\[")[0]; diff --git a/connector/src/main/resources/mappings b/connector/src/main/resources/mappings index 2e52b01cc..bf0610450 160000 --- a/connector/src/main/resources/mappings +++ b/connector/src/main/resources/mappings @@ -1 +1 @@ -Subproject commit 2e52b01cc541c8925346f93be8940087d9af1661 +Subproject commit bf0610450ce94507a18286e94af2965550ff9eaa