Mirror von
https://github.com/GeyserMC/Geyser.git
synchronisiert 2024-11-04 23:30:17 +01:00
Projectile Movement (#1929)
Fixes #1780 #1778 The laggy fireballs Llama spit Shulker bullets
Dieser Commit ist enthalten in:
Ursprung
c4573bb73d
Commit
b7828267a5
@ -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);
|
||||
}
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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),
|
||||
|
@ -177,24 +177,24 @@ public class CollisionManager {
|
||||
session.sendUpstreamPacket(movePlayerPacket);
|
||||
}
|
||||
|
||||
public List<Vector3i> getPlayerCollidableBlocks() {
|
||||
public List<Vector3i> getCollidableBlocks(BoundingBox box) {
|
||||
List<Vector3i> 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<Vector3i> 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
|
||||
|
@ -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<ServerSpawnEntit
|
||||
type, position, motion, rotation, (HangingDirection) packet.getData());
|
||||
} else if (packet.getType() == EntityType.FISHING_BOBBER) {
|
||||
// Fishing bobbers need the owner for the line
|
||||
entity = new FishingHookEntity(packet.getEntityId(), session.getEntityCache().getNextEntityId().incrementAndGet(),
|
||||
type, position, motion, rotation, (ProjectileData) packet.getData());
|
||||
int ownerEntityId = ((ProjectileData) packet.getData()).getOwnerId();
|
||||
Entity owner = session.getEntityCache().getEntityByJavaId(ownerEntityId);
|
||||
if (owner == null && session.getPlayerEntity().getEntityId() == ownerEntityId) {
|
||||
owner = session.getPlayerEntity();
|
||||
}
|
||||
// Java clients only spawn fishing hooks with a player as its owner
|
||||
if (owner instanceof PlayerEntity) {
|
||||
entity = new FishingHookEntity(packet.getEntityId(), session.getEntityCache().getNextEntityId().incrementAndGet(),
|
||||
type, position, motion, rotation, (PlayerEntity) owner);
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
} else if (packet.getType() == EntityType.BOAT) {
|
||||
// Initial rotation is incorrect
|
||||
entity = new BoatEntity(packet.getEntityId(), session.getEntityCache().getNextEntityId().incrementAndGet(),
|
||||
|
@ -49,70 +49,72 @@ public class BlockStateValues {
|
||||
private static final Int2ByteMap SKULL_ROTATIONS = new Int2ByteOpenHashMap();
|
||||
private static final Int2IntMap SKULL_WALL_DIRECTIONS = new Int2IntOpenHashMap();
|
||||
private static final Int2ByteMap SHULKERBOX_DIRECTIONS = new Int2ByteOpenHashMap();
|
||||
private static final Int2IntMap WATER_LEVEL = new Int2IntOpenHashMap();
|
||||
|
||||
/**
|
||||
* Determines if the block state contains Bedrock block information
|
||||
*
|
||||
* @param entry The String to JsonNode map used in BlockTranslator
|
||||
* @param javaId The Java Identifier of the block
|
||||
* @param javaBlockState the Java Block State of the block
|
||||
* @param blockData JsonNode of info about the block from blocks.json
|
||||
*/
|
||||
public static void storeBlockStateValues(Map.Entry<String, JsonNode> 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);
|
||||
}
|
||||
}
|
||||
|
@ -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];
|
||||
|
||||
|
@ -1 +1 @@
|
||||
Subproject commit 2e52b01cc541c8925346f93be8940087d9af1661
|
||||
Subproject commit bf0610450ce94507a18286e94af2965550ff9eaa
|
Laden…
In neuem Issue referenzieren
Einen Benutzer sperren