Skip to content

Commit

Permalink
Projectile Movement (GeyserMC#1929)
Browse files Browse the repository at this point in the history
Fixes

    GeyserMC#1780
    GeyserMC#1778
    The laggy fireballs
    Llama spit
    Shulker bullets
  • Loading branch information
davchoo committed Feb 19, 2021
1 parent c4573bb commit b782826
Show file tree
Hide file tree
Showing 11 changed files with 331 additions and 77 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -47,4 +49,20 @@ public void updateBedrockMetadata(EntityMetadata entityMetadata, GeyserSession s

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);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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<Vector3i> 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;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -32,19 +32,44 @@
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;
}
}

@Override
public void tick(GeyserSession session) {
private Vector3f tickMovement(GeyserSession session, Vector3f position) {
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);
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) {
moveAbsoluteImmediate(session, tickMovement(session, position), rotation, false, false);
}
}
Loading

0 comments on commit b782826

Please sign in to comment.