From 0e15aa7441eff940c8b5f0bbb475eb46b5c0af15 Mon Sep 17 00:00:00 2001 From: Camotoy <20743703+DoctorMacc@users.noreply.github.com> Date: Thu, 5 Nov 2020 18:42:33 -0500 Subject: [PATCH] Fireball and ghast improvements (#1469) * Fireball and ghast improvements - Ghasts now visually show if they're charging a fireball - Fireballs are now vastly better and will update better * Add gravity and drag to projectiles * Add check for session close and improve fireball * Remove motion stuff from fireball * Make fireball hittable * Add wither skull entity * Small changes * Add note about laggy fireballs Co-authored-by: David Choo --- .../entity/ItemedFireballEntity.java | 18 +++- .../connector/entity/ThrowableEntity.java | 97 ++++++++++++++++--- .../connector/entity/WitherSkullEntity.java | 58 +++++++++++ .../entity/living/monster/GhastEntity.java | 49 ++++++++++ .../connector/entity/type/EntityType.java | 6 +- 5 files changed, 212 insertions(+), 16 deletions(-) create mode 100644 connector/src/main/java/org/geysermc/connector/entity/WitherSkullEntity.java create mode 100644 connector/src/main/java/org/geysermc/connector/entity/living/monster/GhastEntity.java 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 e04e0411..1544f767 100644 --- a/connector/src/main/java/org/geysermc/connector/entity/ItemedFireballEntity.java +++ b/connector/src/main/java/org/geysermc/connector/entity/ItemedFireballEntity.java @@ -27,10 +27,24 @@ 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; -public class ItemedFireballEntity extends Entity { +public class ItemedFireballEntity extends ThrowableEntity { + private final Vector3f acceleration; public ItemedFireballEntity(long entityId, long geyserId, EntityType entityType, Vector3f position, Vector3f motion, Vector3f rotation) { - super(entityId, geyserId, entityType, position, motion, rotation); + super(entityId, geyserId, entityType, position, Vector3f.ZERO, rotation); + acceleration = motion; + } + + @Override + protected void updatePosition(GeyserSession session) { + position = position.add(motion); + // TODO: While this reduces latency in position updating (needed for better fireball reflecting), + // TODO: movement is incredibly stiff. See if the MoveEntityDeltaPacket in 1.16.100 fixes this, and if not, + // 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); } } 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 b3632606..5b7ba5c0 100644 --- a/connector/src/main/java/org/geysermc/connector/entity/ThrowableEntity.java +++ b/connector/src/main/java/org/geysermc/connector/entity/ThrowableEntity.java @@ -31,14 +31,23 @@ import com.nukkitx.protocol.bedrock.data.entity.EntityFlag; import com.nukkitx.protocol.bedrock.packet.LevelEventPacket; import org.geysermc.connector.entity.type.EntityType; import org.geysermc.connector.network.session.GeyserSession; +import org.geysermc.connector.network.translators.world.block.BlockTranslator; import java.util.concurrent.ScheduledFuture; import java.util.concurrent.TimeUnit; +/** + * Used as a class for any object-like entity that moves as a projectile + */ public class ThrowableEntity extends Entity { private Vector3f lastPosition; - private ScheduledFuture positionUpdater; + /** + * Updates the position for the Bedrock client. + * + * Java clients assume the next positions of moving items. Bedrock needs to be explicitly told positions + */ + protected ScheduledFuture positionUpdater; public ThrowableEntity(long entityId, long geyserId, EntityType entityType, Vector3f position, Vector3f motion, Vector3f rotation) { super(entityId, geyserId, entityType, position, motion, rotation); @@ -49,20 +58,86 @@ public class ThrowableEntity extends Entity { public void spawnEntity(GeyserSession session) { super.spawnEntity(session); positionUpdater = session.getConnector().getGeneralThreadPool().scheduleAtFixedRate(() -> { - super.moveRelative(session, motion.getX(), motion.getY(), motion.getZ(), rotation, onGround); - - if (metadata.getFlags().getFlag(EntityFlag.HAS_GRAVITY)) { - float gravity = 0.03f; // Snowball, Egg, and Ender Pearl - if (entityType == EntityType.THROWN_POTION || entityType == EntityType.LINGERING_POTION) { - gravity = 0.05f; - } else if (entityType == EntityType.THROWN_EXP_BOTTLE) { - gravity = 0.07f; - } - motion = motion.down(gravity); + if (session.isClosed()) { + positionUpdater.cancel(true); + return; } + updatePosition(session); }, 0, 50, TimeUnit.MILLISECONDS); } + protected void moveAbsoluteImmediate(GeyserSession session, Vector3f position, Vector3f rotation, boolean isOnGround, boolean teleported) { + super.moveAbsolute(session, position, rotation, isOnGround, teleported); + } + + protected void updatePosition(GeyserSession session) { + super.moveRelative(session, motion.getX(), motion.getY(), motion.getZ(), rotation, onGround); + float drag = getDrag(session); + float gravity = getGravity(); + motion = motion.mul(drag).down(gravity); + } + + /** + * Get the gravity of this entity type. Used for applying gravity while the entity is in motion. + * + * @return the amount of gravity to apply to this entity while in motion. + */ + protected float getGravity() { + if (metadata.getFlags().getFlag(EntityFlag.HAS_GRAVITY)) { + switch (entityType) { + case THROWN_POTION: + case LINGERING_POTION: + return 0.05f; + case THROWN_EXP_BOTTLE: + return 0.07f; + case FIREBALL: + return 0; + case SNOWBALL: + case THROWN_EGG: + case THROWN_ENDERPEARL: + return 0.03f; + } + } + return 0; + } + + /** + * @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)) { + return 0.8f; + } else { + switch (entityType) { + case THROWN_POTION: + case LINGERING_POTION: + case THROWN_EXP_BOTTLE: + case SNOWBALL: + case THROWN_EGG: + case THROWN_ENDERPEARL: + return 0.99f; + case FIREBALL: + case SMALL_FIREBALL: + case DRAGON_FIREBALL: + return 0.95f; + } + } + return 1; + } + + /** + * @param session the session of the Bedrock client. + * @return true if this entity is currently in water. + */ + 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; + } + return false; + } + @Override public boolean despawnEntity(GeyserSession session) { positionUpdater.cancel(true); diff --git a/connector/src/main/java/org/geysermc/connector/entity/WitherSkullEntity.java b/connector/src/main/java/org/geysermc/connector/entity/WitherSkullEntity.java new file mode 100644 index 00000000..99b3df3d --- /dev/null +++ b/connector/src/main/java/org/geysermc/connector/entity/WitherSkullEntity.java @@ -0,0 +1,58 @@ +/* + * Copyright (c) 2019-2020 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.nukkitx.math.vector.Vector3f; +import org.geysermc.connector.entity.type.EntityType; +import org.geysermc.connector.network.session.GeyserSession; + +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); + } + + @Override + protected float getDrag(GeyserSession session) { + return isCharged ? 0.73f : super.getDrag(session); + } + + @Override + public void updateBedrockMetadata(EntityMetadata entityMetadata, GeyserSession session) { + if (entityMetadata.getId() == 7) { + 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); + } +} diff --git a/connector/src/main/java/org/geysermc/connector/entity/living/monster/GhastEntity.java b/connector/src/main/java/org/geysermc/connector/entity/living/monster/GhastEntity.java new file mode 100644 index 00000000..3d3be87c --- /dev/null +++ b/connector/src/main/java/org/geysermc/connector/entity/living/monster/GhastEntity.java @@ -0,0 +1,49 @@ +/* + * Copyright (c) 2019-2020 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.monster; + +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.living.FlyingEntity; +import org.geysermc.connector.entity.type.EntityType; +import org.geysermc.connector.network.session.GeyserSession; + +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); + } + + @Override + public void updateBedrockMetadata(EntityMetadata entityMetadata, GeyserSession session) { + if (entityMetadata.getId() == 15) { + // If the ghast is attacking + metadata.put(EntityData.CHARGE_AMOUNT, (byte) ((boolean) entityMetadata.getValue() ? 1 : 0)); + } + super.updateBedrockMetadata(entityMetadata, session); + } +} 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 fddab5a4..7557bd04 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 @@ -74,7 +74,7 @@ public enum EntityType { ENDERMAN(EndermanEntity.class, 38, 2.9f, 0.6f), SILVERFISH(MonsterEntity.class, 39, 0.3f, 0.4f), CAVE_SPIDER(MonsterEntity.class, 40, 0.5f, 0.7f), - GHAST(FlyingEntity.class, 41, 4.0f), + GHAST(GhastEntity.class, 41, 4.0f), MAGMA_CUBE(MagmaCubeEntity.class, 42, 0.51f), BLAZE(BlazeEntity.class, 43, 1.8f, 0.6f), ZOMBIE_VILLAGER(ZombieEntity.class, 44, 1.8f, 0.6f, 0.6f, 1.62f), @@ -125,9 +125,9 @@ public enum EntityType { THROWN_POTION(ThrowableEntity.class, 86, 0.25f, 0.25f, 0.25f, 0f, "minecraft:splash_potion"), THROWN_ENDERPEARL(ThrowableEntity.class, 87, 0.25f, 0.25f, 0.25f, 0f, "minecraft:ender_pearl"), LEASH_KNOT(LeashKnotEntity.class, 88, 0.5f, 0.375f), - WITHER_SKULL(Entity.class, 89, 0.3125f), + WITHER_SKULL(WitherSkullEntity.class, 89, 0.3125f), BOAT(BoatEntity.class, 90, 0.7f, 1.6f, 1.6f, 0.35f), - WITHER_SKULL_DANGEROUS(Entity.class, 91, 0f), + WITHER_SKULL_DANGEROUS(WitherSkullEntity.class, 91, 0f), LIGHTNING_BOLT(Entity.class, 93, 0f), SMALL_FIREBALL(ItemedFireballEntity.class, 94, 0.3125f), AREA_EFFECT_CLOUD(AreaEffectCloudEntity.class, 95, 0.5f, 1.0f),