diff --git a/api/src/main/java/org/allaymc/api/block/component/BlockBaseComponent.java b/api/src/main/java/org/allaymc/api/block/component/BlockBaseComponent.java index b98d8913c..88f0940fb 100644 --- a/api/src/main/java/org/allaymc/api/block/component/BlockBaseComponent.java +++ b/api/src/main/java/org/allaymc/api/block/component/BlockBaseComponent.java @@ -1,6 +1,5 @@ package org.allaymc.api.block.component; -import com.google.common.base.Preconditions; import org.allaymc.api.block.BlockBehavior; import org.allaymc.api.block.data.BlockFace; import org.allaymc.api.block.dto.BlockStateWithPos; @@ -165,7 +164,7 @@ default double calculateBreakTime(BlockState blockState, ItemStack usedItem, Ent var efficiencyLevel = 0; if (entity != null) { - isInWater = entity.isInWater(); + isInWater = entity.isEyesInWater(); isOnGround = entity.isOnGround(); hasteEffectLevel = entity.getEffectLevel(EffectTypes.HASTE); // Conduit Power ensures at least level 2 haste effect diff --git a/api/src/main/java/org/allaymc/api/entity/component/EntityBaseComponent.java b/api/src/main/java/org/allaymc/api/entity/component/EntityBaseComponent.java index 54e2e3235..3d9ff303e 100644 --- a/api/src/main/java/org/allaymc/api/entity/component/EntityBaseComponent.java +++ b/api/src/main/java/org/allaymc/api/entity/component/EntityBaseComponent.java @@ -1,9 +1,8 @@ package org.allaymc.api.entity.component; import org.allaymc.api.block.data.BlockFace; +import org.allaymc.api.block.tag.BlockTags; import org.allaymc.api.block.type.BlockState; -import org.allaymc.api.block.type.BlockType; -import org.allaymc.api.block.type.BlockTypes; import org.allaymc.api.command.CommandSender; import org.allaymc.api.entity.Entity; import org.allaymc.api.entity.effect.EffectInstance; @@ -14,6 +13,7 @@ import org.allaymc.api.entity.type.EntityType; import org.allaymc.api.item.ItemStack; import org.allaymc.api.math.location.Location3fc; +import org.allaymc.api.utils.MathUtils; import org.allaymc.api.world.Dimension; import org.allaymc.api.world.World; import org.allaymc.api.world.chunk.Chunk; @@ -27,6 +27,7 @@ import org.cloudburstmc.protocol.bedrock.packet.AnimatePacket; import org.cloudburstmc.protocol.bedrock.packet.BedrockPacket; import org.cloudburstmc.protocol.bedrock.packet.EntityEventPacket; +import org.jetbrains.annotations.ApiStatus; import org.jetbrains.annotations.UnmodifiableView; import org.joml.Vector3f; import org.joml.Vector3fc; @@ -48,243 +49,568 @@ public interface EntityBaseComponent extends EntityComponent, CommandSender, Has float DEFAULT_PUSH_SPEED_REDUCTION = 1f; float DEFAULT_KNOCKBACK = 0.4f; - private static boolean isWaterType(BlockType blockType) { - return blockType == BlockTypes.FLOWING_WATER || blockType == BlockTypes.WATER; - } - + /** + * Gets the type of this entity. + * + * @return the type of this entity. + */ EntityType getEntityType(); + /** + * Gets the display name of this entity. + * + * @return the display name of this entity. + */ String getDisplayName(); + /** + * Sets the display name of this entity. + * + * @param displayName the display name to set. + */ void setDisplayName(String displayName); + /** + * Gets the name tag of this entity. + * + * @return the name tag of this entity. + */ default String getNameTag() { return getMetadata().get(EntityDataTypes.NAME); } + /** + * Sets the name tag of this entity. + * + * @param nameTag the name tag to set. + */ default void setNameTag(String nameTag) { setAndSendEntityData(EntityDataTypes.NAME, nameTag); } + /** + * Clears the name tag of this entity. + */ default void clearNameTag() { setAndSendEntityData(EntityDataTypes.NAME, ""); } + /** + * Gets the location of this entity. + * + * @return the location of this entity. + */ Location3fc getLocation(); /** * Set the location before the entity is spawned. *

* This method is usually used when you want to spawn the entity at a specific location. - *

* Then you need to set the entity's location before spawn the entity. * - * @param location The location you want to set - * + * @param location the location you want to set * @throws IllegalStateException if the entity is already spawned */ void setLocationBeforeSpawn(Location3fc location); + /** + * Gets the dimension of this entity. + * + * @return the dimension of this entity. + */ Dimension getDimension(); - World getWorld(); + /** + * Gets the world of this entity. + * + * @return the world of this entity. + */ + default World getWorld() { + return getLocation().dimension() != null ? getLocation().dimension().getWorld() : null; + } + /** + * Check if the entity will be spawned in the next tick. + * + * @return {@code true} if the entity will be spawned in the next tick. + */ boolean willBeSpawnedNextTick(); + /** + * Check if the entity will be despawned in the next tick. + * + * @return {@code true} if the entity will be despawned in the next tick. + */ boolean willBeDespawnedNextTick(); + /** + * Check if the entity is dead. + * + * @return {@code true} if the entity is dead. + */ boolean isDead(); + /** + * Check if the entity is spawned. + * + * @return {@code true} if the entity is spawned. + */ boolean isSpawned(); + /** + * Check if the entity is alive. + * + * @return {@code true} if the entity is alive. + */ default boolean isAlive() { return isSpawned() && !isDead(); } + /** + * Check if the entity can be spawned. + * + * @return {@code true} if the entity can be spawned. + */ boolean canBeSpawned(); + /** + * Teleport the entity to the specified location. + * + * @param location the location to teleport the entity to. + */ void teleport(Location3fc location); + /** + * Get the runtime id of this entity. + * + * @return the runtime id of this entity. + */ long getRuntimeId(); + /** + * Same to {@link #getRuntimeId()}. + */ @Override default long getLongId() { return getRuntimeId(); } + /** + * Get the unique id of this entity. + * + * @return the unique id of this entity. + */ long getUniqueId(); + /** + * Get the metadata of this entity. + * + * @return the metadata of this entity. + */ Metadata getMetadata(); + /** + * Send the entity data to the viewers. + * + * @param dataTypes the data types to send. + */ void sendEntityData(EntityDataType... dataTypes); + /** + * Send the entity flags to the viewers. + * + * @param flags the flags to send. + */ void sendEntityFlags(EntityFlag... flags); + /** + * Set and send the entity data to the viewers. + * + * @param dataType the data type to set. + * @param value the value to set. + * @param the type of the value. + */ default void setAndSendEntityData(EntityDataType dataType, T value) { getMetadata().set(dataType, value); sendEntityData(dataType); } + /** + * Set and send the entity flag to the viewers. + * + * @param flag the flag to set. + * @param value the value to set. + */ default void setAndSendEntityFlag(EntityFlag flag, boolean value) { if (value == getMetadata().get(flag)) return; getMetadata().set(flag, value); sendEntityFlags(flag); } - default void addAndSendEntityFlags(EntityFlag... flags) { - for (EntityFlag flag : flags) { - getMetadata().set(flag, true); - } - sendEntityFlags(flags); - } + /** + * Get the aabb of this entity. + * + * @return the aabb of this entity. + */ + AABBfc getAABB(); - default void removeAndSendEntityFlags(EntityFlag... flags) { - for (EntityFlag flag : flags) { - getMetadata().set(flag, false); - } - sendEntityFlags(flags); + /** + * Get the offset aabb of this entity. + * + * @return the offset aabb of this entity. + */ + default AABBf getOffsetAABB() { + return getAABB().translate(getLocation(), new AABBf()); } - AABBfc getAABB(); - + /** + * Check if the entity has collision. + * + * @return {@code true} if the entity has collision. + */ default boolean hasEntityCollision() { return getMetadata().get(EntityFlag.HAS_COLLISION); } + /** + * Set if the entity has collision. + * + * @param hasEntityCollision {@code true} if the entity has collision. + */ default void setHasEntityCollision(boolean hasEntityCollision) { setAndSendEntityFlag(EntityFlag.HAS_COLLISION, hasEntityCollision); } + /** + * Check if the entity has entity collision motion. + * + * @return {@code true} if the entity has entity collision motion. + */ default boolean computeEntityCollisionMotion() { return hasEntityCollision(); } + /** + * Check if the entity has block collision motion. + * + * @return {@code true} if the entity has block collision motion. + */ default boolean computeBlockCollisionMotion() { return false; } + /** + * Called when the entity collides with another entity. + * + * @param other the entity collides with. + */ + @ApiStatus.OverrideOnly default void onCollideWith(Entity other) {} + /** + * Get the viewers of this entity. + * + * @return the viewers of this entity. + */ @UnmodifiableView Map getViewers(); + /** + * Get the motion of this entity. + * + * @return the motion of this entity. + */ Vector3fc getMotion(); + /** + * Set the motion of this entity. + * + * @param motion the motion to set. + */ void setMotion(Vector3fc motion); - Vector3fc getLastMotion(); - + /** + * Set the motion of this entity. + * + * @param mx the motion x to set. + * @param my the motion y to set. + * @param mz the motion z to set. + */ default void setMotion(float mx, float my, float mz) { setMotion(new Vector3f(mx, my, mz)); } + /** + * Add the motion to this entity. + * + * @param add the motion to add. + */ default void addMotion(Vector3fc add) { setMotion(getMotion().add(add, new Vector3f())); } + /** + * Add the motion to this entity. + * + * @param mx the motion x to add. + * @param my the motion y to add. + * @param mz the motion z to add. + */ default void addMotion(float mx, float my, float mz) { setMotion(getMotion().add(mx, my, mz, new Vector3f())); } - boolean isOnGround(); + /** + * Get the last motion of this entity. + * + * @return the last motion of this entity. + */ + Vector3fc getLastMotion(); - void setOnGround(boolean onGround); + /** + * Check if the entity is on the ground. + * + * @return {@code true} if the entity is on the ground, otherwise {@code false}. + */ + boolean isOnGround(); + /** + * Spawn the entity to the specified player. + * + * @param player the player to spawn the entity to. + */ void spawnTo(EntityPlayer player); + /** + * Spawn the entity to the specified players. + * + * @param players the players to spawn the entity to. + */ default void spawnTo(Set players) { players.forEach(this::spawnTo); } + /** + * Despawn the entity from the specified player. + * + * @param player the player to despawn the entity from. + */ void despawnFrom(EntityPlayer player); + /** + * Despawn the entity from all viewers. + *

+ * This method will only remove the entity from the viewers, but the entity will still exist in the world. + */ void despawnFromAll(); + /** + * Despawn the entity. + *

+ * Compared to {@link #despawnFromAll()}, this method will also remove the entity from the world. + */ void despawn(); + /** + * Create the spawn packet of this entity. + * + * @return the spawn packet of this entity. + */ BedrockPacket createSpawnPacket(); + /** + * Send a packet to the viewers of this entity. + * + * @param packet the packet to send. + */ void sendPacketToViewers(BedrockPacket packet); + /** + * Send a packet to the viewers of this entity immediately. + * + * @param packet the packet to send. + */ void sendPacketToViewersImmediately(BedrockPacket packet); - void broadcastMoveToViewers(Location3fc newLoc); - - void broadcastMoveToViewers(Location3fc newLoc, boolean teleporting); - + /** + * Save the entity to NBT. + * + * @return the NBT of this entity. + */ NbtMap saveNBT(); + /** + * Save the entity to NBT without position. + * + * @return the NBT of this entity without position. + */ default NbtMap saveNBTWithoutPos() { var builder = saveNBT().toBuilder(); builder.remove("Pos"); return builder.build(); } + /** + * Load the entity from NBT. + * + * @param nbt the NBT to load. + */ void loadNBT(NbtMap nbt); + /** + * Get the fall distance of this entity. + * + * @return the fall distance of this entity. + */ float getFallDistance(); + /** + * Called when the entity falls. + */ + @ApiStatus.OverrideOnly void onFall(); + /** + * Check if the entity has the specified effect. + * + * @param effectType the effect type to check. + * @return {@code true} if the entity has the specified effect, otherwise {@code false}. + */ boolean hasEffect(EffectType effectType); + /** + * Get the effect level of the specified effect. + * + * @param effectType the effect type to get. + * @return the effect level of the specified effect. + */ int getEffectLevel(EffectType effectType); + /** + * Add the specified effect to the entity. + * + * @param effectInstance the effect instance to add. + */ void addEffect(EffectInstance effectInstance); + /** + * Remove the specified effect from the entity. + * + * @param effectType the effect type to remove. + */ void removeEffect(EffectType effectType); + /** + * Remove all effects from the entity. + */ void removeAllEffects(); + /** + * Called when the entity ticks. + * + * @param currentTick the current tick. + */ + @ApiStatus.OverrideOnly default void tick(long currentTick) {} + /** + * Check if the entity has head yaw. + * + * @return {@code true} if the entity has head yaw, otherwise {@code false}. + */ default boolean enableHeadYaw() { return false; } + /** + * Get the base offset of this entity. + * + * @return the base offset of this entity. + */ default float getBaseOffset() { return 0f; } - default AABBf getOffsetAABB() { - return getAABB().translate(getLocation(), new AABBf()); - } - + /** + * Check whether the entity's movement should be computed server-side. + * + * @return {@code true} if the entity's movement should be computed server-side, otherwise {@code false}. + */ default boolean computeMovementServerSide() { return true; } + /** + * Get the step height of this entity. + * + * @return the step height of this entity. + */ default float getStepHeight() { return 0.6f; } + /** + * Get the gravity of this entity. + * + * @return the gravity of this entity. + */ default float getGravity() { return 0.08f; } - default float getEyeHeight() { - return (getAABB().maxY() - getAABB().minY()) * 0.9f; - } - + /** + * Check if the entity has gravity. + * + * @return {@code true} if the entity has gravity, otherwise {@code false}. + */ default boolean hasGravity() { return getMetadata().get(EntityFlag.HAS_GRAVITY); } - void setHasGravity(boolean hasGravity); + /** + * Set if the entity has gravity. + * + * @param hasGravity {@code true} if the entity has gravity, otherwise {@code false}. + */ + default void setHasGravity(boolean hasGravity) { + setAndSendEntityFlag(EntityFlag.HAS_GRAVITY, hasGravity); + } + + /** + * Get the eye height of this entity. + * + * @return the eye height of this entity. + */ + default float getEyeHeight() { + return (getAABB().maxY() - getAABB().minY()) * 0.9f; + } /** - * Given yaw, if the movement multiplier is not 0, the entity will move towards the direction specified by yaw.

+ * Get the movement factor of this entity. + *

+ * Given yaw, if the movement multiplier is not 0, the entity will move towards the direction specified by yaw. * + * @return the movement factor of this entity. * @see Horizontal Movement Formulas */ default float getMovementFactor() { return STOP_MOVEMENT_FACTOR; } + /** + * Get the push speed reduction of this entity. + * + * @return the push speed reduction of this entity. + */ default float getPushSpeedReduction() { return DEFAULT_PUSH_SPEED_REDUCTION; } + /** + * Check if the chunk which the entity's location is in is loaded. + * + * @return {@code true} if the chunk is loaded, otherwise {@code false}. + */ default boolean isCurrentChunkLoaded() { var loc = getLocation(); var cx = (int) loc.x() >> 4; @@ -292,15 +618,30 @@ default boolean isCurrentChunkLoaded() { return loc.dimension().getChunkService().isChunkLoaded(cx, cz); } + /** + * Check if the y coordinate of the entity's location is in the range of the dimension. + * + * @return {@code true} if the y coordinate is in the range, otherwise {@code false}. + */ default boolean isYInRange() { var loc = getLocation(); return loc.dimension().isYInRange(loc.y()); } + /** + * Check if the entity is in the world. + * + * @return {@code true} if the entity is in the world, otherwise {@code false}. + */ default boolean isInWorld() { return isYInRange() && isCurrentChunkLoaded(); } + /** + * Get the current chunk of the entity. + * + * @return the current chunk of the entity. + */ default Chunk getCurrentChunk() { var loc = getLocation(); var cx = (int) loc.x() >> 4; @@ -308,6 +649,11 @@ default Chunk getCurrentChunk() { return loc.dimension().getChunkService().getChunk(cx, cz); } + /** + * Get the horizontal face of the entity. + * + * @return the horizontal face of the entity. + */ default BlockFace getHorizontalFace() { var rotation = getLocation().yaw() % 360; if (rotation < 0) rotation += 360.0; @@ -323,16 +669,40 @@ default BlockFace getHorizontalFace() { } } + /** + * Knockback the entity. + * + * @param source the source of the knockback. + */ default void knockback(Vector3fc source) { knockback(source, DEFAULT_KNOCKBACK); } + /** + * Knockback the entity. + * + * @param source the source of the knockback. + * @param kb the knockback strength to apply. + */ default void knockback(Vector3fc source, float kb) { knockback(source, kb, false); } + /** + * Knockback the entity. + * + * @param source the source of the knockback. + * @param kb the knockback strength to apply. + * @param ignoreKnockbackResistance {@code true} if the knockback resistance should be ignored. + */ void knockback(Vector3fc source, float kb, boolean ignoreKnockbackResistance); + /** + * Apply the entity event to the entity. + * + * @param event the entity event to apply. + * @param data the data of the entity event. + */ default void applyEntityEvent(EntityEventType event, int data) { var pk = new EntityEventPacket(); pk.setRuntimeEntityId(getRuntimeId()); @@ -341,11 +711,22 @@ default void applyEntityEvent(EntityEventType event, int data) { sendPacketToViewers(pk); } - default void applyAnimation(AnimatePacket.Action action) { - applyAnimation(action, 0); + /** + * Apply an action to the entity. + * + * @param action the action to apply. + */ + default void applyAction(AnimatePacket.Action action) { + applyAction(action, 0); } - default void applyAnimation(AnimatePacket.Action action, float rowingTime) { + /** + * Apply an action to the entity. + * + * @param action the action of the action. + * @param rowingTime the rowing time of the action. + */ + default void applyAction(AnimatePacket.Action action, float rowingTime) { var pk = new AnimatePacket(); pk.setRuntimeEntityId(getRuntimeId()); pk.setAction(action); @@ -353,37 +734,81 @@ default void applyAnimation(AnimatePacket.Action action, float rowingTime) { sendPacketToViewers(pk); } + /** + * Check if the entity can critical attack. + * + * @return {@code true} if the entity can critical attack, otherwise {@code false}. + */ default boolean canCriticalAttack() { return !isOnGround() && getMotion().y() < 0 && !hasEffect(EffectTypes.BLINDNESS) && !hasEffect(EffectTypes.SLOW_FALLING); } + /** + * Add a tag to the entity. + * + * @param tag the tag to add. + * @return {@code true} if the tag is added, otherwise {@code false}. + */ boolean addTag(String tag); + /** + * Remove a tag from the entity. + * + * @param tag the tag to remove. + * @return {@code true} if the tag is removed, otherwise {@code false}. + */ boolean removeTag(String tag); + /** + * Check if the entity has the specified tag. + * + * @param tag the tag to check. + * @return {@code true} if the entity has the specified tag, otherwise {@code false}. + */ boolean hasTag(String tag); + /** + * Get the tags of the entity. + * + * @return the tags of the entity. + */ @UnmodifiableView Set getTags(); - default boolean isInWater() { - var loc = getLocation(); - var blockType = getDimension().getBlockState(loc).getBlockType(); - if (isWaterType(blockType)) return true; + /** + * Check if the entity's eyes is in water. + * + * @return {@code true} if the entity is in water, otherwise {@code false}. + */ + default boolean isEyesInWater() { + var dim = getDimension(); + var eyeLoc = getLocation().add(0, getEyeHeight(), 0, new Vector3f()); + var currentBlockState0 = dim.getBlockState(eyeLoc); + var currentBlockState1 = dim.getBlockState(eyeLoc, 1); + + if (!currentBlockState0.getBlockStateData().hasCollision() && currentBlockState1.getBlockType().hasBlockTag(BlockTags.WATER)) { + return true; + } - blockType = getDimension().getBlockState(loc, 1).getBlockType(); - return isWaterType(blockType); + return currentBlockState0.getBlockType().hasBlockTag(BlockTags.WATER) && + currentBlockState0.getBlockStateData().computeOffsetCollisionShape(MathUtils.floor(eyeLoc)).intersectsPoint(eyeLoc); } /** + * Called when the entity interacts with another entity. + * * @param player The player who interacted with the entity, can be null * @param itemStack The item used to interact with the entity - * * @return {@code true} if the interaction is successful */ default boolean onInteract(EntityPlayer player, ItemStack itemStack) { return false; } + /** + * Get the block state which the entity is standing on. + * + * @return the block state which the entity is standing on. + */ BlockState getBlockStateStandingOn(); } diff --git a/api/src/main/java/org/allaymc/api/entity/damage/DamageContainer.java b/api/src/main/java/org/allaymc/api/entity/damage/DamageContainer.java index 606273102..13efd33ac 100644 --- a/api/src/main/java/org/allaymc/api/entity/damage/DamageContainer.java +++ b/api/src/main/java/org/allaymc/api/entity/damage/DamageContainer.java @@ -18,6 +18,9 @@ import static org.allaymc.api.entity.damage.DamageContainer.DamageType.*; /** + * DamageContainer is a container that stores the information of the damage, + * including the attacker, the damage type, the source damage, the final damage, etc. + * * @author daoge_cmd */ @Getter @@ -54,40 +57,93 @@ public DamageContainer(Object attacker, DamageType damageType, float sourceDamag this.finalDamage = sourceDamage; } + /** + * Create a simple attack damage container. + * + * @param sourceDamage the source damage. + * @return the damage container. + */ public static DamageContainer simpleAttack(float sourceDamage) { return new DamageContainer(null, API, sourceDamage); } + /** + * Create an entity attack damage container. + * + * @param attacker the attacker. + * @param sourceDamage the source damage. + * @return the damage container. + */ public static DamageContainer entityAttack(Entity attacker, float sourceDamage) { var damageContainer = new DamageContainer(attacker, ENTITY_ATTACK, sourceDamage); damageContainer.setCritical(attacker.canCriticalAttack()); return damageContainer; } + /** + * Create a starve damage container. + * + * @param sourceDamage the source damage. + * @return the damage container. + */ public static DamageContainer starve(float sourceDamage) { return new DamageContainer(null, STARVE, sourceDamage); } + /** + * Create a fall damage container. + * + * @param sourceDamage the source damage. + * @return the damage container. + */ public static DamageContainer fall(float sourceDamage) { return new DamageContainer(null, FALL, sourceDamage); } + /** + * Create a magic effect damage container. + * + * @param sourceDamage the source damage. + * @return the damage container. + */ public static DamageContainer magicEffect(float sourceDamage) { return new DamageContainer(null, MAGIC, sourceDamage); } + /** + * Get the attacker. + * + * @return the attacker, or {@code null} if the attacker is not present. + * @param the type of the attacker. + */ public T getAttacker() { + // noinspection unchecked return (T) attacker; } + /** + * Check if the damage has a custom knockback. + * + * @return {@code true} if the damage has a custom knockback, otherwise {@code false}. + */ public boolean hasCustomKnockback() { return customKnockback != -1; } + /** + * Update the final damage using the given updater. + * + * @param updater the updater. + */ public void updateFinalDamage(UnaryOperator updater) { this.finalDamage = updater.apply(this.finalDamage); } + /** + * Check if the damage can be reduced by armor. + * + * @return {@code true} if the damage can be reduced by armor, otherwise {@code false}. + */ public boolean canBeReducedByArmor() { return !CANNOT_BE_REDUCED_BY_ARMOR_DAMAGE_TYPES.contains(damageType); } diff --git a/api/src/main/java/org/allaymc/api/item/component/data/ItemDataComponent.java b/api/src/main/java/org/allaymc/api/item/component/data/ItemDataComponent.java index 6cc91f478..6681a197e 100644 --- a/api/src/main/java/org/allaymc/api/item/component/data/ItemDataComponent.java +++ b/api/src/main/java/org/allaymc/api/item/component/data/ItemDataComponent.java @@ -6,5 +6,10 @@ * @author daoge_cmd */ public interface ItemDataComponent extends ItemComponent { + /** + * Get the item data. + * + * @return The item data. + */ ItemData getItemData(); } diff --git a/server/src/main/java/org/allaymc/server/entity/component/EntityBaseComponentImpl.java b/server/src/main/java/org/allaymc/server/entity/component/EntityBaseComponentImpl.java index c9443de05..5d468f92d 100644 --- a/server/src/main/java/org/allaymc/server/entity/component/EntityBaseComponentImpl.java +++ b/server/src/main/java/org/allaymc/server/entity/component/EntityBaseComponentImpl.java @@ -4,6 +4,7 @@ import lombok.Getter; import lombok.Setter; import lombok.extern.slf4j.Slf4j; +import org.allaymc.api.block.tag.BlockTags; import org.allaymc.api.block.type.BlockState; import org.allaymc.api.block.type.BlockTypes; import org.allaymc.api.command.CommandSender; @@ -246,11 +247,7 @@ protected void setLocation(Location3fc location, boolean calculateFallDistance) // fall distance < 0 -> move up // fall distance > 0 -> move down this.fallDistance -= location.y() - this.location.y(); - var currentBlockState = location.dimension().getBlockState(location); - if (currentBlockState.getBlockStateData().canResetFallDistance() && - currentBlockState.getBlockStateData().computeOffsetCollisionShape(MathUtils.floor(location)).intersectsAABB(getAABB().translate(location, new AABBf()))) { - this.fallDistance = 0; - } + tryResetFallDistance(location); } this.location.set(location); @@ -260,14 +257,24 @@ protected void setLocation(Location3fc location, boolean calculateFallDistance) this.location.setDimension(location.dimension()); } - @Override - public Dimension getDimension() { - return location.dimension; + private void tryResetFallDistance(Location3fc location) { + var currentBlockState0 = location.dimension().getBlockState(location); + var currentBlockState1 = location.dimension().getBlockState(location, 1); + + if (!currentBlockState0.getBlockStateData().hasCollision() && currentBlockState1.getBlockType().hasBlockTag(BlockTags.WATER)) { + this.fallDistance = 0; + return; + } + + if (currentBlockState0.getBlockStateData().canResetFallDistance() && + currentBlockState0.getBlockStateData().computeOffsetCollisionShape(MathUtils.floor(location)).intersectsAABB(getAABB().translate(location, new AABBf()))) { + this.fallDistance = 0; + } } @Override - public World getWorld() { - return location.dimension != null ? location.dimension.getWorld() : null; + public Dimension getDimension() { + return location.dimension; } @Override @@ -433,7 +440,6 @@ public void setMotion(Vector3fc motion) { this.motion = new Vector3f(motion); } - @Override public void setOnGround(boolean onGround) { this.onGround = onGround; if (onGround && this.fallDistance > 0) { @@ -441,11 +447,6 @@ public void setOnGround(boolean onGround) { } } - @Override - public void setHasGravity(boolean hasGravity) { - setAndSendEntityFlag(EntityFlag.HAS_GRAVITY, hasGravity); - } - @Override public void knockback(Vector3fc source, float kb, boolean ignoreKnockbackResistance) { motion = calculateKnockbackMotion(source, kb, ignoreKnockbackResistance); @@ -519,12 +520,6 @@ public void sendPacketToViewersImmediately(BedrockPacket packet) { viewers.values().forEach(client -> client.sendPacketImmediately(packet)); } - @Override - public void broadcastMoveToViewers(Location3fc newLoc) { - broadcastMoveToViewers(newLoc, false); - } - - @Override public void broadcastMoveToViewers(Location3fc newLoc, boolean teleporting) { var movementPk = createMovePacket(newLoc, teleporting); var motionPk = createMotionPacket(); diff --git a/server/src/main/java/org/allaymc/server/entity/component/EntityDamageComponentImpl.java b/server/src/main/java/org/allaymc/server/entity/component/EntityDamageComponentImpl.java index 218edf893..c3dea3b94 100644 --- a/server/src/main/java/org/allaymc/server/entity/component/EntityDamageComponentImpl.java +++ b/server/src/main/java/org/allaymc/server/entity/component/EntityDamageComponentImpl.java @@ -67,7 +67,7 @@ public boolean attack(DamageContainer damage) { protected void applyDamage(DamageContainer damage) { attributeComponent.setHealth(attributeComponent.getHealth() - damage.getFinalDamage()); baseComponent.applyEntityEvent(EntityEventType.HURT, 2); - if (damage.isCritical()) baseComponent.applyAnimation(AnimatePacket.Action.CRITICAL_HIT); + if (damage.isCritical()) baseComponent.applyAction(AnimatePacket.Action.CRITICAL_HIT); manager.callEvent(CEntityAfterDamageEvent.INSTANCE); diff --git a/server/src/main/java/org/allaymc/server/entity/component/player/EntityPlayerBaseComponentImpl.java b/server/src/main/java/org/allaymc/server/entity/component/player/EntityPlayerBaseComponentImpl.java index d5f64821c..86fa310e3 100644 --- a/server/src/main/java/org/allaymc/server/entity/component/player/EntityPlayerBaseComponentImpl.java +++ b/server/src/main/java/org/allaymc/server/entity/component/player/EntityPlayerBaseComponentImpl.java @@ -637,7 +637,7 @@ public void applyEntityEvent(EntityEventType event, int data) { } @Override - public void applyAnimation(AnimatePacket.Action action, float rowingTime) { + public void applyAction(AnimatePacket.Action action, float rowingTime) { var packet = new AnimatePacket(); packet.setRuntimeEntityId(getRuntimeId()); packet.setAction(action); diff --git a/server/src/main/java/org/allaymc/server/world/service/AllayEntityPhysicsService.java b/server/src/main/java/org/allaymc/server/world/service/AllayEntityPhysicsService.java index a48fdc8d3..9a8a422af 100644 --- a/server/src/main/java/org/allaymc/server/world/service/AllayEntityPhysicsService.java +++ b/server/src/main/java/org/allaymc/server/world/service/AllayEntityPhysicsService.java @@ -293,7 +293,7 @@ protected boolean applyMotion(Entity entity) { // Update onGround status after updated entity location // to make sure that some block (for example: water) can reset // entity's fallDistance before onFall() called - entity.setOnGround(isOnGround); + entity.getManager().getComponent(EntityBaseComponentImpl.IDENTIFIER).setOnGround(isOnGround); return true; } return false; @@ -486,7 +486,7 @@ protected void handleClientMoveQueue() { // If it's a server-calculated move, the onGround status will be calculated in applyMotion() var aabb = clientMove.player.getOffsetAABB(); aabb.minY -= FAT_AABB_MARGIN; - player.setOnGround(dimension.getCollidingBlocks(aabb) != null); + player.getManager().getComponent(EntityBaseComponentImpl.IDENTIFIER).setOnGround(dimension.getCollidingBlocks(aabb) != null); } } } @@ -499,7 +499,7 @@ protected boolean updateEntityLocation(Entity entity, Location3fc newLoc) { } newLoc = event.getTo(); - entity.broadcastMoveToViewers(newLoc); + entity.getManager().getComponent(EntityBaseComponentImpl.IDENTIFIER).broadcastMoveToViewers(newLoc, false); entity.getManager().getComponent(EntityBaseComponentImpl.IDENTIFIER).setLocationAndCheckChunk(newLoc); return true; }