mirror of
https://github.com/GeyserMC/Geyser.git
synced 2025-12-19 14:59:27 +00:00
Expose entity hitboxes in API, store bedrock position to avoid re-calculation
This commit is contained in:
@@ -1,5 +1,7 @@
|
||||
package org.geysermc.geyser.api.entity.data;
|
||||
|
||||
import org.geysermc.geyser.api.entity.data.types.Hitbox;
|
||||
|
||||
/**
|
||||
* Contains commonly used {@link GeyserEntityDataType} constants for built-in entity
|
||||
* metadata fields.
|
||||
@@ -48,6 +50,12 @@ public final class GeyserEntityDataTypes {
|
||||
public static final GeyserEntityDataType<Float> SCALE =
|
||||
GeyserEntityDataType.of(Float.class, "scale");
|
||||
|
||||
/**
|
||||
* Represents custom hitboxes for entities
|
||||
*/
|
||||
public static final GeyserListEntityDataType<Hitbox> HITBOXES =
|
||||
GeyserListEntityDataType.of(Hitbox.class, "hitboxes");
|
||||
|
||||
private GeyserEntityDataTypes() {
|
||||
// no-op
|
||||
}
|
||||
|
||||
@@ -0,0 +1,48 @@
|
||||
/*
|
||||
* Copyright (c) 2025 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.geyser.api.entity.data;
|
||||
|
||||
import org.geysermc.geyser.api.GeyserApi;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Represents a list of objects for an entity data types
|
||||
* For example, there can be multiple hitboxes on an entity
|
||||
*
|
||||
* @param <T>
|
||||
*/
|
||||
public interface GeyserListEntityDataType<T> extends GeyserEntityDataType<List<T>> {
|
||||
|
||||
Class<T> listTypeClass();
|
||||
|
||||
/**
|
||||
* API usage only, use the types defined in {@link GeyserEntityDataTypes}
|
||||
*/
|
||||
static <T> GeyserListEntityDataType<T> of(Class<T> typeClass, String name) {
|
||||
return GeyserApi.api().provider(GeyserListEntityDataType.class, List.class, typeClass, name);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,71 @@
|
||||
/*
|
||||
* Copyright (c) 2025 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.geyser.api.entity.data.types;
|
||||
|
||||
import org.cloudburstmc.math.vector.Vector3f;
|
||||
import org.geysermc.geyser.api.GeyserApi;
|
||||
|
||||
/**
|
||||
* Represents an entity hitbox.
|
||||
*/
|
||||
public interface Hitbox {
|
||||
|
||||
/**
|
||||
* The min "corner" of the hitbox
|
||||
* @return the vector of the corner
|
||||
*/
|
||||
Vector3f min();
|
||||
|
||||
/**
|
||||
* The max "corner" of the hitbox
|
||||
* @return the vector of the corner
|
||||
*/
|
||||
Vector3f max();
|
||||
|
||||
/**
|
||||
* The pivot of the hitbox
|
||||
* @return
|
||||
*/
|
||||
Vector3f pivot();
|
||||
|
||||
static Builder builder() {
|
||||
return GeyserApi.api().provider(Builder.class);
|
||||
}
|
||||
|
||||
/**
|
||||
* The builder for the hitbox
|
||||
*/
|
||||
interface Builder {
|
||||
|
||||
Builder min(Vector3f min);
|
||||
|
||||
Builder max(Vector3f max);
|
||||
|
||||
Builder origin(Vector3f pivot);
|
||||
|
||||
Hitbox build();
|
||||
}
|
||||
}
|
||||
@@ -113,7 +113,9 @@ public class Entity implements GeyserEntity {
|
||||
* The entity position as it is known to the Java server
|
||||
*/
|
||||
@Accessors(fluent = true)
|
||||
protected Vector3f position;
|
||||
private Vector3f position;
|
||||
@Setter(AccessLevel.NONE)
|
||||
private Vector3f bedrockPosition;
|
||||
protected Vector3f motion;
|
||||
|
||||
/**
|
||||
@@ -708,8 +710,13 @@ public class Entity implements GeyserEntity {
|
||||
return this.valid;
|
||||
}
|
||||
|
||||
public void position(Vector3f position) {
|
||||
this.position = position;
|
||||
this.bedrockPosition = position.up(offset);
|
||||
}
|
||||
|
||||
public Vector3f bedrockPosition() {
|
||||
return position.up(offset);
|
||||
return bedrockPosition;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -860,10 +867,10 @@ public class Entity implements GeyserEntity {
|
||||
}
|
||||
}
|
||||
|
||||
public void offset(float offset) {
|
||||
public void offset(float offset, boolean teleport) {
|
||||
this.offset = offset;
|
||||
// TODO queue?
|
||||
if (isValid()) {
|
||||
if (isValid() && teleport) {
|
||||
this.moveRelative(0, 0, 0, 0, 0, isOnGround());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -64,7 +64,7 @@ public class FireballEntity extends ThrowableEntity {
|
||||
newPosition = tickMovement(newPosition);
|
||||
}
|
||||
super.moveAbsoluteImmediate(newPosition, yaw, pitch, headYaw, isOnGround, teleported);
|
||||
this.position = javaPosition;
|
||||
position(javaPosition);
|
||||
this.motion = lastMotion;
|
||||
}
|
||||
|
||||
@@ -73,6 +73,6 @@ public class FireballEntity extends ThrowableEntity {
|
||||
if (removedInVoid()) {
|
||||
return;
|
||||
}
|
||||
moveAbsoluteImmediate(tickMovement(position), getYaw(), getPitch(), getHeadYaw(), false, false);
|
||||
moveAbsoluteImmediate(tickMovement(position()), getYaw(), getPitch(), getHeadYaw(), false, false);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -113,7 +113,7 @@ public class FishingHookEntity extends ThrowableEntity {
|
||||
if (!collided) {
|
||||
super.moveAbsoluteImmediate(javaPosition, yaw, pitch, headYaw, isOnGround, teleported);
|
||||
} else {
|
||||
super.moveAbsoluteImmediate(this.position, yaw, pitch, headYaw, true, true);
|
||||
super.moveAbsoluteImmediate(this.position(), yaw, pitch, headYaw, true, true);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -144,7 +144,7 @@ public class FishingHookEntity extends ThrowableEntity {
|
||||
float gravity = getGravity();
|
||||
motion = motion.down(gravity);
|
||||
|
||||
moveAbsoluteImmediate(position.add(motion), getYaw(), getPitch(), getHeadYaw(), isOnGround(), false);
|
||||
moveAbsoluteImmediate(position().add(motion), getYaw(), getPitch(), getHeadYaw(), isOnGround(), false);
|
||||
|
||||
float drag = getDrag();
|
||||
motion = motion.mul(drag);
|
||||
@@ -162,7 +162,7 @@ public class FishingHookEntity extends ThrowableEntity {
|
||||
* @return true if this entity is currently in air.
|
||||
*/
|
||||
protected boolean isInAir() {
|
||||
int block = session.getGeyser().getWorldManager().getBlockAt(session, position.toInt());
|
||||
int block = session.getGeyser().getWorldManager().getBlockAt(session, position().toInt());
|
||||
return block == Block.JAVA_AIR_ID;
|
||||
}
|
||||
|
||||
|
||||
@@ -119,7 +119,7 @@ public class InteractionEntity extends Entity {
|
||||
|
||||
@Override
|
||||
public void moveRelative(double relX, double relY, double relZ, float yaw, float pitch, float headYaw, boolean isOnGround) {
|
||||
moveAbsolute(position.add(relX, relY, relZ), yaw, pitch, headYaw, isOnGround, false);
|
||||
moveAbsolute(position().add(relX, relY, relZ), yaw, pitch, headYaw, isOnGround, false);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -141,7 +141,7 @@ public class InteractionEntity extends Entity {
|
||||
setBoundingBoxHeight(Math.min(height.getPrimitiveValue(), 64f));
|
||||
|
||||
if (secondEntity != null) {
|
||||
secondEntity.moveAbsolute(position.up(getBoundingBoxHeight()), yaw, pitch, onGround, true);
|
||||
secondEntity.moveAbsolute(position().up(getBoundingBoxHeight()), yaw, pitch, onGround, true);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -159,7 +159,7 @@ public class InteractionEntity extends Entity {
|
||||
}
|
||||
|
||||
if (this.secondEntity == null) {
|
||||
secondEntity = new ArmorStandEntity(EntitySpawnContext.inherited(session, VanillaEntities.ARMOR_STAND, this, position.up(getBoundingBoxHeight())));
|
||||
secondEntity = new ArmorStandEntity(EntitySpawnContext.inherited(session, VanillaEntities.ARMOR_STAND, this, position().up(getBoundingBoxHeight())));
|
||||
}
|
||||
secondEntity.getDirtyMetadata().put(EntityDataTypes.NAME, nametag);
|
||||
secondEntity.getDirtyMetadata().put(EntityDataTypes.NAMETAG_ALWAYS_SHOW, isNameTagVisible ? (byte) 1 : (byte) 0);
|
||||
|
||||
@@ -79,7 +79,7 @@ public class ItemEntity extends ThrowableEntity {
|
||||
if (!isOnGround() || (motion.getX() * motion.getX() + motion.getZ() * motion.getZ()) > 0.00001) {
|
||||
float gravity = getGravity();
|
||||
motion = motion.down(gravity);
|
||||
moveAbsoluteImmediate(position.add(motion), getYaw(), getPitch(), getHeadYaw(), isOnGround(), false);
|
||||
moveAbsoluteImmediate(position().add(motion), getYaw(), getPitch(), getHeadYaw(), isOnGround(), false);
|
||||
float drag = getDrag();
|
||||
motion = motion.mul(drag, 0.98f, drag);
|
||||
}
|
||||
@@ -118,7 +118,7 @@ public class ItemEntity extends ThrowableEntity {
|
||||
this.offset = Math.abs(offset);
|
||||
}
|
||||
super.moveAbsoluteImmediate(javaPosition, 0, 0, 0, isOnGround, teleported);
|
||||
this.position = javaPosition;
|
||||
position(javaPosition);
|
||||
|
||||
waterLevel = session.getGeyser().getWorldManager().getBlockAtAsync(session, javaPosition.getFloorX(), javaPosition.getFloorY(), javaPosition.getFloorZ())
|
||||
.thenApply(BlockStateValues::getWaterLevel);
|
||||
@@ -137,7 +137,7 @@ public class ItemEntity extends ThrowableEntity {
|
||||
@Override
|
||||
protected float getDrag() {
|
||||
if (isOnGround()) {
|
||||
Vector3i groundBlockPos = position.toInt().down();
|
||||
Vector3i groundBlockPos = position().toInt().down();
|
||||
BlockState blockState = session.getGeyser().getWorldManager().blockAt(session, groundBlockPos);
|
||||
return BlockStateValues.getSlipperiness(blockState) * 0.98f;
|
||||
}
|
||||
|
||||
@@ -80,7 +80,7 @@ public class ItemFrameEntity extends HangingEntity {
|
||||
super(context);
|
||||
|
||||
blockDefinition = buildBlockDefinition(Direction.SOUTH); // Default to SOUTH direction, like on Java - entity metadata should correct this when necessary
|
||||
bedrockPosition = Vector3i.from(position.getFloorX(), position.getFloorY(), position.getFloorZ());
|
||||
bedrockPosition = position().floor().toInt();
|
||||
|
||||
session.getItemFrameCache().put(bedrockPosition, this);
|
||||
}
|
||||
|
||||
@@ -35,7 +35,7 @@ public class LeashKnotEntity extends Entity {
|
||||
super(context);
|
||||
// Position is incorrect by default
|
||||
// TODO offset
|
||||
position(position.add(0.5f, 0.25f, 0.5f));
|
||||
position(position().add(0.5f, 0.25f, 0.5f));
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -194,7 +194,7 @@ public class MinecartEntity extends Entity implements Tickable {
|
||||
}
|
||||
|
||||
private void updateCompletedStep() {
|
||||
lastCompletedStep = new MinecartStep(position.toDouble(), motion.toDouble(), yaw, pitch, 0.0F);
|
||||
lastCompletedStep = new MinecartStep(position().toDouble(), motion.toDouble(), yaw, pitch, 0.0F);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -92,7 +92,7 @@ public class PaintingEntity extends HangingEntity {
|
||||
|
||||
valid = true;
|
||||
|
||||
session.getGeyser().getLogger().debug("Spawned painting on " + position);
|
||||
session.getGeyser().getLogger().debug("Spawned painting on " + position());
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -101,7 +101,7 @@ public class PaintingEntity extends HangingEntity {
|
||||
}
|
||||
|
||||
private Vector3f fixOffset(PaintingType paintingName) {
|
||||
Vector3f position = super.position;
|
||||
Vector3f position = position();
|
||||
// ViaVersion already adds the offset for us on older versions,
|
||||
// so no need to do it then otherwise it will be spaced
|
||||
if (session.isEmulatePost1_18Logic()) {
|
||||
|
||||
@@ -90,7 +90,7 @@ public class TextDisplayEntity extends DisplayBaseEntity {
|
||||
|
||||
// If the line count changed, update the position to account for the new offset
|
||||
if (previousLineCount != lineCount) {
|
||||
moveAbsolute(position, yaw, pitch, headYaw, onGround, false);
|
||||
moveAbsolute(position(), yaw, pitch, headYaw, onGround, false);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -43,7 +43,7 @@ public class ThrowableEntity extends Entity implements Tickable {
|
||||
|
||||
public ThrowableEntity(EntitySpawnContext context) {
|
||||
super(context);
|
||||
this.lastJavaPosition = position;
|
||||
this.lastJavaPosition = position();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -55,7 +55,7 @@ public class ThrowableEntity extends Entity implements Tickable {
|
||||
if (removedInVoid()) {
|
||||
return;
|
||||
}
|
||||
moveAbsoluteImmediate(position.add(motion), getYaw(), getPitch(), getHeadYaw(), isOnGround(), false);
|
||||
moveAbsoluteImmediate(position().add(motion), getYaw(), getPitch(), getHeadYaw(), isOnGround(), false);
|
||||
float drag = getDrag();
|
||||
float gravity = getGravity();
|
||||
motion = motion.mul(drag).down(gravity);
|
||||
@@ -75,15 +75,15 @@ public class ThrowableEntity extends Entity implements Tickable {
|
||||
moveEntityDeltaPacket.getFlags().add(MoveEntityDeltaPacket.Flag.TELEPORTING);
|
||||
}
|
||||
|
||||
if (this.position.getX() != javaPosition.getX()) {
|
||||
if (this.position().getX() != javaPosition.getX()) {
|
||||
moveEntityDeltaPacket.getFlags().add(MoveEntityDeltaPacket.Flag.HAS_X);
|
||||
moveEntityDeltaPacket.setX(javaPosition.getX());
|
||||
}
|
||||
if (this.position.getY() != javaPosition.getY()) {
|
||||
if (this.position().getY() != javaPosition.getY()) {
|
||||
moveEntityDeltaPacket.getFlags().add(MoveEntityDeltaPacket.Flag.HAS_Y);
|
||||
moveEntityDeltaPacket.setY(javaPosition.getY() + offset);
|
||||
}
|
||||
if (this.position.getZ() != javaPosition.getZ()) {
|
||||
if (this.position().getZ() != javaPosition.getZ()) {
|
||||
moveEntityDeltaPacket.getFlags().add(MoveEntityDeltaPacket.Flag.HAS_Z);
|
||||
moveEntityDeltaPacket.setZ(javaPosition.getZ());
|
||||
}
|
||||
@@ -155,7 +155,7 @@ public class ThrowableEntity extends Entity implements Tickable {
|
||||
* @return true if this entity is currently in water.
|
||||
*/
|
||||
protected boolean isInWater() {
|
||||
int block = session.getGeyser().getWorldManager().getBlockAt(session, position.toInt());
|
||||
int block = session.getGeyser().getWorldManager().getBlockAt(session, position().toInt());
|
||||
return BlockStateValues.getWaterLevel(block) != -1;
|
||||
}
|
||||
|
||||
@@ -173,7 +173,7 @@ public class ThrowableEntity extends Entity implements Tickable {
|
||||
@Override
|
||||
public void moveRelative(double relX, double relY, double relZ, float yaw, float pitch, float headYaw, boolean isOnGround) {
|
||||
moveAbsoluteImmediate(lastJavaPosition.add(relX, relY, relZ), yaw, pitch, headYaw, isOnGround, false);
|
||||
lastJavaPosition = position;
|
||||
lastJavaPosition = position();
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -188,7 +188,7 @@ public class ThrowableEntity extends Entity implements Tickable {
|
||||
* @return true if the entity was removed
|
||||
*/
|
||||
public boolean removedInVoid() {
|
||||
if (position.getY() < session.getDimensionType().minY() - 64) {
|
||||
if (position().getY() < session.getDimensionType().minY() - 64) {
|
||||
session.getEntityCache().removeEntity(this);
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -92,11 +92,9 @@ public class ArmorStandEntity extends LivingEntity {
|
||||
|
||||
@Override
|
||||
public void spawnEntity() {
|
||||
Vector3f javaPosition = position;
|
||||
// Apply the offset if we're the second entity
|
||||
position = position.up(getYOffset());
|
||||
offset(getYOffset(), false);
|
||||
super.spawnEntity();
|
||||
position = javaPosition;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -109,7 +107,7 @@ public class ArmorStandEntity extends LivingEntity {
|
||||
|
||||
@Override
|
||||
public void moveRelative(double relX, double relY, double relZ, float yaw, float pitch, float headYaw, boolean isOnGround) {
|
||||
moveAbsolute(position.add(relX, relY, relZ), yaw, pitch, headYaw, onGround, false);
|
||||
moveAbsolute(position().add(relX, relY, relZ), yaw, pitch, headYaw, onGround, false);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -118,9 +116,8 @@ public class ArmorStandEntity extends LivingEntity {
|
||||
secondEntity.moveAbsolute(javaPosition, yaw, pitch, headYaw, isOnGround, teleported);
|
||||
}
|
||||
// Fake the height to be above where it is so the nametag appears in the right location
|
||||
float yOffset = getYOffset();
|
||||
super.moveAbsolute(yOffset != 0 ? javaPosition.up(yOffset) : javaPosition, yaw, yaw, yaw, isOnGround, teleported);
|
||||
this.position = javaPosition;
|
||||
offset(getYOffset(), false);
|
||||
super.moveAbsolute(javaPosition, yaw, yaw, yaw, isOnGround, teleported);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -240,7 +237,7 @@ public class ArmorStandEntity extends LivingEntity {
|
||||
super.updateBedrockMetadata();
|
||||
if (positionUpdateRequired) {
|
||||
positionUpdateRequired = false;
|
||||
moveAbsolute(position, yaw, pitch, headYaw, onGround, true);
|
||||
moveAbsolute(position(), yaw, pitch, headYaw, onGround, true);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -341,8 +338,7 @@ public class ArmorStandEntity extends LivingEntity {
|
||||
if (secondEntity == null) {
|
||||
// Create the second entity. It doesn't need to worry about the items, but it does need to worry about
|
||||
// the metadata as it will hold the name tag.
|
||||
// TODO
|
||||
secondEntity = new ArmorStandEntity(EntitySpawnContext.inherited(session, VanillaEntities.ARMOR_STAND, this, position));
|
||||
secondEntity = new ArmorStandEntity(EntitySpawnContext.inherited(session, VanillaEntities.ARMOR_STAND, this, position()));
|
||||
secondEntity.primaryEntity = false;
|
||||
}
|
||||
// Copy metadata
|
||||
|
||||
@@ -128,7 +128,7 @@ public class SquidEntity extends AgeableWaterEntity implements Tickable {
|
||||
if (getFlag(EntityFlag.RIDING)) {
|
||||
inWater = CompletableFuture.completedFuture(false);
|
||||
} else {
|
||||
inWater = session.getGeyser().getWorldManager().getBlockAtAsync(session, position.toInt())
|
||||
inWater = session.getGeyser().getWorldManager().getBlockAtAsync(session, position().toInt())
|
||||
.thenApply(block -> BlockStateValues.getWaterLevel(block) != -1);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -52,7 +52,7 @@ public class OcelotEntity extends AnimalEntity {
|
||||
@NonNull
|
||||
@Override
|
||||
protected InteractiveTag testMobInteraction(@NonNull Hand hand, @NonNull GeyserItemStack itemInHand) {
|
||||
if (!getFlag(EntityFlag.TRUSTING) && canEat(itemInHand) && session.getPlayerEntity().position().distanceSquared(position) < 9f) {
|
||||
if (!getFlag(EntityFlag.TRUSTING) && canEat(itemInHand) && session.getPlayerEntity().position().distanceSquared(position()) < 9f) {
|
||||
// Attempt to feed
|
||||
return InteractiveTag.FEED;
|
||||
} else {
|
||||
@@ -63,7 +63,7 @@ public class OcelotEntity extends AnimalEntity {
|
||||
@NonNull
|
||||
@Override
|
||||
protected InteractionResult mobInteract(@NonNull Hand hand, @NonNull GeyserItemStack itemInHand) {
|
||||
if (!getFlag(EntityFlag.TRUSTING) && canEat(itemInHand) && session.getPlayerEntity().position().distanceSquared(position) < 9f) {
|
||||
if (!getFlag(EntityFlag.TRUSTING) && canEat(itemInHand) && session.getPlayerEntity().position().distanceSquared(position()) < 9f) {
|
||||
// Attempt to feed
|
||||
return InteractionResult.SUCCESS;
|
||||
} else {
|
||||
|
||||
@@ -102,7 +102,7 @@ public class SnifferEntity extends AnimalEntity implements Tickable {
|
||||
// The java client renders digging particles on its own, but bedrock does not
|
||||
if (digTicks > 0 && --digTicks < DIG_START && digTicks % 5 == 0) {
|
||||
Vector3f rot = Vector3f.createDirectionDeg(0, -getYaw()).mul(2.25f);
|
||||
Vector3f pos = position.add(rot).up(0.2f).floor(); // Handle non-full blocks
|
||||
Vector3f pos = position().add(rot).up(0.2f).floor(); // Handle non-full blocks
|
||||
int blockId = session.getBlockMappings().getBedrockBlockId(session.getGeyser().getWorldManager().getBlockAt(session, pos.toInt().down()));
|
||||
|
||||
LevelEventPacket levelEventPacket = new LevelEventPacket();
|
||||
|
||||
@@ -149,12 +149,12 @@ public class VillagerEntity extends AbstractMerchantEntity {
|
||||
setPitch(pitch);
|
||||
setHeadYaw(headYaw);
|
||||
setOnGround(isOnGround);
|
||||
this.position = Vector3f.from(position.getX() + relX, position.getY() + relY, position.getZ() + relZ);
|
||||
position(Vector3f.from(position().getX() + relX, position().getY() + relY, position().getZ() + relZ));
|
||||
|
||||
MoveEntityAbsolutePacket moveEntityPacket = new MoveEntityAbsolutePacket();
|
||||
moveEntityPacket.setRuntimeEntityId(geyserId);
|
||||
moveEntityPacket.setRotation(Vector3f.from(0, 0, bedRotation));
|
||||
moveEntityPacket.setPosition(Vector3f.from(position.getX() + xOffset, position.getY() + offset, position.getZ() + zOffset));
|
||||
moveEntityPacket.setPosition(position().add(xOffset, offset, zOffset));
|
||||
moveEntityPacket.setOnGround(isOnGround);
|
||||
moveEntityPacket.setTeleported(false);
|
||||
session.sendUpstreamPacket(moveEntityPacket);
|
||||
|
||||
@@ -134,7 +134,7 @@ public class EnderDragonEntity extends MobEntity implements Tickable {
|
||||
for (int i = 0; i < segmentHistory.length; i++) {
|
||||
segmentHistory[i] = new Segment();
|
||||
segmentHistory[i].yaw = getHeadYaw();
|
||||
segmentHistory[i].y = position.getY();
|
||||
segmentHistory[i].y = position().getY();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -206,7 +206,7 @@ public class EnderDragonEntity extends MobEntity implements Tickable {
|
||||
}
|
||||
// Send updated positions
|
||||
for (EnderDragonPartEntity part : allParts) {
|
||||
part.moveAbsolute(part.position().add(position), 0, 0, 0, false, false);
|
||||
part.moveAbsolute(part.position().add(position()), 0, 0, 0, false, false);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -277,7 +277,7 @@ public class EnderDragonEntity extends MobEntity implements Tickable {
|
||||
float xOffset = 8f * (random.nextFloat() - 0.5f);
|
||||
float yOffset = 4f * (random.nextFloat() - 0.5f) + 2f;
|
||||
float zOffset = 8f * (random.nextFloat() - 0.5f);
|
||||
Vector3f particlePos = position.add(xOffset, yOffset, zOffset);
|
||||
Vector3f particlePos = position().add(xOffset, yOffset, zOffset);
|
||||
LevelEventPacket particlePacket = new LevelEventPacket();
|
||||
particlePacket.setType(ParticleType.EXPLODE);
|
||||
particlePacket.setPosition(particlePos);
|
||||
@@ -311,7 +311,7 @@ public class EnderDragonEntity extends MobEntity implements Tickable {
|
||||
private void pushSegment() {
|
||||
latestSegment = (latestSegment + 1) % segmentHistory.length;
|
||||
segmentHistory[latestSegment].yaw = getHeadYaw();
|
||||
segmentHistory[latestSegment].y = position.getY();
|
||||
segmentHistory[latestSegment].y = position().getY();
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -41,7 +41,6 @@ import org.cloudburstmc.protocol.bedrock.data.entity.EntityFlag;
|
||||
import org.cloudburstmc.protocol.bedrock.packet.AddPlayerPacket;
|
||||
import org.cloudburstmc.protocol.bedrock.packet.MovePlayerPacket;
|
||||
import org.geysermc.geyser.entity.spawn.EntitySpawnContext;
|
||||
import org.geysermc.geyser.entity.type.Entity;
|
||||
import org.geysermc.geyser.entity.type.LivingEntity;
|
||||
import org.geysermc.geyser.level.block.Blocks;
|
||||
import org.geysermc.geyser.skin.SkinManager;
|
||||
@@ -162,7 +161,7 @@ public class AvatarEntity extends LivingEntity {
|
||||
setYaw(yaw);
|
||||
setPitch(pitch);
|
||||
setHeadYaw(headYaw);
|
||||
this.position = Vector3f.from(position.getX() + relX, position.getY() + relY, position.getZ() + relZ);
|
||||
position(Vector3f.from(position().getX() + relX, position().getY() + relY, position().getZ() + relZ));
|
||||
|
||||
setOnGround(isOnGround);
|
||||
|
||||
@@ -175,9 +174,9 @@ public class AvatarEntity extends LivingEntity {
|
||||
// If the player is moved while sleeping, we have to adjust their y, so it appears
|
||||
// correctly on Bedrock. This fixes GSit's lay.
|
||||
if (getFlag(EntityFlag.SLEEPING)) {
|
||||
if (bedPosition != null && (bedPosition.getY() == 0 || bedPosition.distanceSquared(position.toInt()) > 4)) {
|
||||
if (bedPosition != null && (bedPosition.getY() == 0 || bedPosition.distanceSquared(position().toInt()) > 4)) {
|
||||
// Force the player movement by using a teleport
|
||||
movePlayerPacket.setPosition(Vector3f.from(position.getX(), position.getY() - offset + 0.2f, position.getZ()));
|
||||
movePlayerPacket.setPosition(Vector3f.from(position().getX(), position().getY() - offset + 0.2f, position().getZ()));
|
||||
movePlayerPacket.setMode(MovePlayerPacket.Mode.TELEPORT);
|
||||
}
|
||||
}
|
||||
@@ -190,7 +189,7 @@ public class AvatarEntity extends LivingEntity {
|
||||
}
|
||||
|
||||
@Override
|
||||
public Entity position(Vector3f position) {
|
||||
public void position(Vector3f position) {
|
||||
if (this.bedPosition != null) {
|
||||
// As of Bedrock 1.21.22 and Fabric 1.21.1
|
||||
// Messes with Bedrock if we send this to the client itself, though.
|
||||
@@ -198,7 +197,6 @@ public class AvatarEntity extends LivingEntity {
|
||||
} else {
|
||||
super.position(position);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -316,7 +314,7 @@ public class AvatarEntity extends LivingEntity {
|
||||
|
||||
if (pose == Pose.SWIMMING) {
|
||||
// This is just for, so we know if player is swimming or crawling.
|
||||
if (session.getGeyser().getWorldManager().blockAt(session, position.toInt()).is(Blocks.WATER)) {
|
||||
if (session.getGeyser().getWorldManager().blockAt(session, position().toInt()).is(Blocks.WATER)) {
|
||||
setFlag(EntityFlag.SWIMMING, true);
|
||||
} else {
|
||||
setFlag(EntityFlag.CRAWLING, true);
|
||||
|
||||
@@ -161,7 +161,7 @@ public class PlayerEntity extends AvatarEntity implements GeyserPlayerEntity {
|
||||
return;
|
||||
}
|
||||
// The parrot is a separate entity in Bedrock, but part of the player entity in Java
|
||||
EntitySpawnContext context = EntitySpawnContext.inherited(session, VanillaEntities.PARROT, this, position);
|
||||
EntitySpawnContext context = EntitySpawnContext.inherited(session, VanillaEntities.PARROT, this, position());
|
||||
if (context.callParrotEvent(this, variant.getAsInt(), !isLeft)) {
|
||||
GeyserImpl.getInstance().getLogger().debug(session, "Cancelled parrot spawn as definition is null!");
|
||||
return;
|
||||
|
||||
@@ -163,11 +163,11 @@ public class SessionPlayerEntity extends PlayerEntity {
|
||||
@Override
|
||||
public void moveRelative(double relX, double relY, double relZ, float yaw, float pitch, float headYaw, boolean isOnGround) {
|
||||
super.moveRelative(relX, relY, relZ, yaw, pitch, headYaw, isOnGround);
|
||||
session.getCollisionManager().updatePlayerBoundingBox(this.position);
|
||||
session.getCollisionManager().updatePlayerBoundingBox(position());
|
||||
}
|
||||
|
||||
@Override
|
||||
public Entity position(Vector3f position) {
|
||||
public void position(Vector3f position) {
|
||||
if (valid) { // Don't update during session init
|
||||
session.getCollisionManager().updatePlayerBoundingBox(position);
|
||||
|
||||
@@ -175,8 +175,7 @@ public class SessionPlayerEntity extends PlayerEntity {
|
||||
session.setNoClip(false);
|
||||
}
|
||||
}
|
||||
this.position = position;
|
||||
return this;
|
||||
super.position(position);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -212,10 +211,10 @@ public class SessionPlayerEntity extends PlayerEntity {
|
||||
* Set the player's position from a position sent in a Bedrock packet
|
||||
*/
|
||||
public void setPositionFromBedrock(Vector3f position) {
|
||||
this.position = position.down(offset);
|
||||
position(position.down(offset));
|
||||
|
||||
// Player is "above" the void so they're not supposed to no clip.
|
||||
if (session.isNoClip() && this.position.getY() >= session.getBedrockDimension().minY() - 5) {
|
||||
if (session.isNoClip() && position().getY() >= session.getBedrockDimension().minY() - 5) {
|
||||
session.setNoClip(false);
|
||||
}
|
||||
}
|
||||
@@ -497,7 +496,7 @@ public class SessionPlayerEntity extends PlayerEntity {
|
||||
if (session.getGameMode() == GameMode.SPECTATOR) {
|
||||
return false;
|
||||
}
|
||||
BlockState state = session.getGeyser().getWorldManager().blockAt(session, position.toInt());
|
||||
BlockState state = session.getGeyser().getWorldManager().blockAt(session, position().toInt());
|
||||
if (state.block().is(session, BlockTag.CLIMBABLE)) {
|
||||
return true;
|
||||
}
|
||||
@@ -506,7 +505,7 @@ public class SessionPlayerEntity extends PlayerEntity {
|
||||
if (!state.getValue(Properties.OPEN)) {
|
||||
return false;
|
||||
} else {
|
||||
BlockState belowState = session.getGeyser().getWorldManager().blockAt(session, position.toInt().down());
|
||||
BlockState belowState = session.getGeyser().getWorldManager().blockAt(session, position().toInt().down());
|
||||
return belowState.is(Blocks.LADDER) && belowState.getValue(Properties.HORIZONTAL_FACING) == state.getValue(Properties.HORIZONTAL_FACING);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -138,28 +138,28 @@ public class BoatVehicleComponent extends VehicleComponent<BoatEntity> {
|
||||
|
||||
@Override
|
||||
protected void moveVehicle(Vector3d javaPos, Vector3f lastRotation) {
|
||||
Vector3f bedrockPos = javaPos.toFloat();
|
||||
Vector3f oldPosition = vehicle.position();
|
||||
vehicle.position(javaPos.toFloat());
|
||||
|
||||
MoveEntityDeltaPacket moveEntityDeltaPacket = new MoveEntityDeltaPacket();
|
||||
moveEntityDeltaPacket.setRuntimeEntityId(vehicle.getGeyserId());
|
||||
moveEntityDeltaPacket.setRuntimeEntityId(vehicle.geyserId());
|
||||
|
||||
if (vehicle.isOnGround()) {
|
||||
moveEntityDeltaPacket.getFlags().add(MoveEntityDeltaPacket.Flag.ON_GROUND);
|
||||
}
|
||||
|
||||
if (vehicle.getPosition().getX() != bedrockPos.getX()) {
|
||||
if (vehicle.position().getX() != oldPosition.getX()) {
|
||||
moveEntityDeltaPacket.getFlags().add(MoveEntityDeltaPacket.Flag.HAS_X);
|
||||
moveEntityDeltaPacket.setX(bedrockPos.getX());
|
||||
moveEntityDeltaPacket.setX(vehicle.bedrockPosition().getX());
|
||||
}
|
||||
if (vehicle.getPosition().getY() != bedrockPos.getY()) {
|
||||
if (vehicle.position().getY() != oldPosition.getY()) {
|
||||
moveEntityDeltaPacket.getFlags().add(MoveEntityDeltaPacket.Flag.HAS_Y);
|
||||
moveEntityDeltaPacket.setY(bedrockPos.getY() + vehicle.getDefinition().offset());
|
||||
moveEntityDeltaPacket.setY(vehicle.bedrockPosition().getY());
|
||||
}
|
||||
if (vehicle.getPosition().getZ() != bedrockPos.getZ()) {
|
||||
if (vehicle.position().getZ() != oldPosition.getZ()) {
|
||||
moveEntityDeltaPacket.getFlags().add(MoveEntityDeltaPacket.Flag.HAS_Z);
|
||||
moveEntityDeltaPacket.setZ(bedrockPos.getZ());
|
||||
moveEntityDeltaPacket.setZ(vehicle.bedrockPosition().getZ());
|
||||
}
|
||||
vehicle.setPosition(bedrockPos);
|
||||
|
||||
if (vehicle.getPitch() != lastRotation.getX()) {
|
||||
moveEntityDeltaPacket.getFlags().add(MoveEntityDeltaPacket.Flag.HAS_PITCH);
|
||||
@@ -175,7 +175,7 @@ public class BoatVehicleComponent extends VehicleComponent<BoatEntity> {
|
||||
}
|
||||
|
||||
if (!moveEntityDeltaPacket.getFlags().isEmpty()) {
|
||||
vehicle.getSession().sendUpstreamPacket(moveEntityDeltaPacket);
|
||||
vehicle.getSession().sendUpstreamPacketImmediately(moveEntityDeltaPacket);
|
||||
}
|
||||
|
||||
ServerboundMoveVehiclePacket moveVehiclePacket = new ServerboundMoveVehiclePacket(javaPos, vehicle.getYaw() - 90, vehicle.getPitch(), vehicle.isOnGround());
|
||||
|
||||
@@ -738,7 +738,8 @@ public class VehicleComponent<T extends Entity & ClientVehicle> {
|
||||
* @param lastRotation the previous rotation of the vehicle (pitch, yaw, headYaw)
|
||||
*/
|
||||
protected void moveVehicle(Vector3d javaPos, Vector3f lastRotation) {
|
||||
Vector3f bedrockPos = javaPos.toFloat();
|
||||
Vector3f oldPosition = vehicle.position();
|
||||
vehicle.position(javaPos.toFloat());
|
||||
|
||||
MoveEntityDeltaPacket moveEntityDeltaPacket = new MoveEntityDeltaPacket();
|
||||
moveEntityDeltaPacket.setRuntimeEntityId(vehicle.geyserId());
|
||||
@@ -747,19 +748,18 @@ public class VehicleComponent<T extends Entity & ClientVehicle> {
|
||||
moveEntityDeltaPacket.getFlags().add(MoveEntityDeltaPacket.Flag.ON_GROUND);
|
||||
}
|
||||
|
||||
if (vehicle.position().getX() != bedrockPos.getX()) {
|
||||
if (vehicle.position().getX() != oldPosition.getX()) {
|
||||
moveEntityDeltaPacket.getFlags().add(MoveEntityDeltaPacket.Flag.HAS_X);
|
||||
moveEntityDeltaPacket.setX(bedrockPos.getX());
|
||||
moveEntityDeltaPacket.setX(vehicle.bedrockPosition().getX());
|
||||
}
|
||||
if (vehicle.position().getY() != bedrockPos.getY()) {
|
||||
if (vehicle.position().getY() != oldPosition.getY()) {
|
||||
moveEntityDeltaPacket.getFlags().add(MoveEntityDeltaPacket.Flag.HAS_Y);
|
||||
moveEntityDeltaPacket.setY(bedrockPos.getY());
|
||||
moveEntityDeltaPacket.setY(vehicle.bedrockPosition().getY());
|
||||
}
|
||||
if (vehicle.position().getZ() != bedrockPos.getZ()) {
|
||||
if (vehicle.position().getZ() != oldPosition.getZ()) {
|
||||
moveEntityDeltaPacket.getFlags().add(MoveEntityDeltaPacket.Flag.HAS_Z);
|
||||
moveEntityDeltaPacket.setZ(bedrockPos.getZ());
|
||||
moveEntityDeltaPacket.setZ(vehicle.bedrockPosition().getZ());
|
||||
}
|
||||
vehicle.position(bedrockPos);
|
||||
|
||||
if (vehicle.getPitch() != lastRotation.getX()) {
|
||||
moveEntityDeltaPacket.getFlags().add(MoveEntityDeltaPacket.Flag.HAS_PITCH);
|
||||
|
||||
@@ -50,7 +50,7 @@ public class GeyserEntityDataImpl<T> implements GeyserEntityDataType<T> {
|
||||
TYPES.put("scale", new GeyserEntityDataImpl<>(Float.class, "scale", EntityDataTypes.SCALE));
|
||||
|
||||
// "custom"
|
||||
TYPES.put("vertical_offset", new GeyserEntityDataImpl<>(Float.class, "offset", Entity::offset, Entity::getOffset));
|
||||
TYPES.put("vertical_offset", new GeyserEntityDataImpl<>(Float.class, "offset", (entity, value) -> entity.offset(value, true), Entity::getOffset));
|
||||
}
|
||||
|
||||
public static GeyserEntityDataImpl<?> lookup(Class<?> clazz, String name) {
|
||||
|
||||
@@ -0,0 +1,82 @@
|
||||
/*
|
||||
* Copyright (c) 2025 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.geyser.impl.entity;
|
||||
|
||||
import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap;
|
||||
import org.cloudburstmc.nbt.NbtMap;
|
||||
import org.cloudburstmc.protocol.bedrock.data.entity.EntityDataTypes;
|
||||
import org.geysermc.geyser.api.entity.data.GeyserListEntityDataType;
|
||||
import org.geysermc.geyser.api.entity.data.types.Hitbox;
|
||||
import org.geysermc.geyser.entity.type.Entity;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.function.BiConsumer;
|
||||
import java.util.function.Function;
|
||||
|
||||
public class GeyserListEntityDataImpl<ListType> extends GeyserEntityDataImpl<List<ListType>> implements GeyserListEntityDataType<ListType> {
|
||||
|
||||
public static Map<String, GeyserListEntityDataImpl<?>> TYPES;
|
||||
static {
|
||||
TYPES = new Object2ObjectOpenHashMap<>();
|
||||
TYPES.put("hitboxes", new GeyserListEntityDataImpl<>(Hitbox.class, "hitboxes",
|
||||
(entity, hitboxes) -> entity.getDirtyMetadata().put(EntityDataTypes.HITBOX, HitboxImpl.toNbtMap(hitboxes)),
|
||||
(entity -> HitboxImpl.fromMetaData((NbtMap) entity.getMetadata().get(EntityDataTypes.HITBOX)))));
|
||||
}
|
||||
|
||||
private final Class<ListType> listTypeClass;
|
||||
|
||||
public GeyserListEntityDataImpl(Class<ListType> typeClass, String name, BiConsumer<Entity, List<ListType>> consumer, Function<Entity, List<ListType>> getter) {
|
||||
//noinspection unchecked - we do not talk about it
|
||||
super((Class<List<ListType>>) (Class<?>) List.class, name, consumer, getter);
|
||||
this.listTypeClass = typeClass;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Class<ListType> listTypeClass() {
|
||||
return listTypeClass;
|
||||
}
|
||||
|
||||
public static GeyserListEntityDataImpl<?> lookup(Class<?> clazz, Class<?> listTypeClass, String name) {
|
||||
Objects.requireNonNull(clazz);
|
||||
Objects.requireNonNull(listTypeClass);
|
||||
Objects.requireNonNull(name);
|
||||
|
||||
if (clazz != List.class) {
|
||||
throw new IllegalStateException("Cannot look up list entity data for " + clazz + " and " + listTypeClass + " for " + name);
|
||||
}
|
||||
|
||||
var type = TYPES.get(name);
|
||||
if (type == null) {
|
||||
throw new IllegalArgumentException("Unknown entity data type: " + name);
|
||||
}
|
||||
if (type.listTypeClass() == listTypeClass) {
|
||||
return TYPES.get(name);
|
||||
}
|
||||
throw new IllegalArgumentException("Unknown entity data type: " + name);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,116 @@
|
||||
/*
|
||||
* Copyright (c) 2025 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.geyser.impl.entity;
|
||||
|
||||
import org.checkerframework.checker.nullness.qual.Nullable;
|
||||
import org.cloudburstmc.math.vector.Vector3f;
|
||||
import org.cloudburstmc.nbt.NbtMap;
|
||||
import org.cloudburstmc.nbt.NbtType;
|
||||
import org.geysermc.geyser.api.entity.data.types.Hitbox;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
|
||||
public record HitboxImpl(
|
||||
Vector3f min,
|
||||
Vector3f max,
|
||||
Vector3f pivot
|
||||
) implements Hitbox {
|
||||
|
||||
public static List<Hitbox> fromMetaData(@Nullable NbtMap metaDataMap) {
|
||||
if (metaDataMap == null) {
|
||||
return List.of();
|
||||
}
|
||||
|
||||
List<Hitbox> boxes = new ArrayList<>();
|
||||
List<NbtMap> hitboxes = metaDataMap.getList("Hitxboxes", NbtType.COMPOUND);
|
||||
for (NbtMap hitbox : hitboxes) {
|
||||
boxes.add(new HitboxImpl(
|
||||
Vector3f.from(hitbox.getFloat("MinX"), hitbox.getFloat("MinY"), hitbox.getFloat("MinZ")),
|
||||
Vector3f.from(hitbox.getFloat("MaxX"), hitbox.getFloat("MaxY"), hitbox.getFloat("MaxZ")),
|
||||
Vector3f.from(hitbox.getFloat("PivotX"),hitbox.getFloat("PivotY"),hitbox.getFloat("PivotZ"))
|
||||
));
|
||||
}
|
||||
return boxes;
|
||||
}
|
||||
|
||||
public NbtMap toNbtMap() {
|
||||
return NbtMap.builder()
|
||||
.putFloat("MinX", min.getX())
|
||||
.putFloat("MinY", min.getY())
|
||||
.putFloat("MinZ", min.getZ())
|
||||
.putFloat("MaxX", max.getX())
|
||||
.putFloat("MaxY", max.getY())
|
||||
.putFloat("MaxZ", max.getZ())
|
||||
.putFloat("PivotX", pivot.getX())
|
||||
.putFloat("PivotY", pivot.getY())
|
||||
.putFloat("PivotZ", pivot.getZ())
|
||||
.build();
|
||||
}
|
||||
|
||||
public static NbtMap toNbtMap(List<Hitbox> hitboxes) {
|
||||
List<NbtMap> list = new ArrayList<>();
|
||||
for (Hitbox hitbox : hitboxes) {
|
||||
if (hitbox instanceof HitboxImpl impl) {
|
||||
list.add(impl.toNbtMap());
|
||||
} else {
|
||||
throw new IllegalArgumentException("Unknown hitbox class implementation: " + hitbox.getClass().getSimpleName());
|
||||
}
|
||||
}
|
||||
return NbtMap.builder().putList("Hitboxes", NbtType.COMPOUND, list).build();
|
||||
}
|
||||
|
||||
public static class Builder implements Hitbox.Builder {
|
||||
Vector3f min, max, pivot;
|
||||
|
||||
@Override
|
||||
public Hitbox.Builder min(Vector3f min) {
|
||||
Objects.requireNonNull(min, "min");
|
||||
this.min = min;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Hitbox.Builder max(Vector3f max) {
|
||||
Objects.requireNonNull(max, "max");
|
||||
this.max = max;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Hitbox.Builder origin(Vector3f pivot) {
|
||||
Objects.requireNonNull(pivot, "pivot");
|
||||
this.pivot = pivot;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Hitbox build() {
|
||||
return new HitboxImpl(min == null ? Vector3f.ZERO : min, max == null ? Vector3f.ZERO : max, pivot == null ? Vector3f.ZERO : pivot);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -35,6 +35,8 @@ import org.geysermc.geyser.api.block.custom.component.MaterialInstance;
|
||||
import org.geysermc.geyser.api.block.custom.nonvanilla.JavaBlockState;
|
||||
import org.geysermc.geyser.api.command.Command;
|
||||
import org.geysermc.geyser.api.entity.data.GeyserEntityDataType;
|
||||
import org.geysermc.geyser.api.entity.data.GeyserListEntityDataType;
|
||||
import org.geysermc.geyser.api.entity.data.types.Hitbox;
|
||||
import org.geysermc.geyser.api.entity.definition.GeyserEntityDefinition;
|
||||
import org.geysermc.geyser.api.entity.definition.JavaEntityType;
|
||||
import org.geysermc.geyser.api.event.EventRegistrar;
|
||||
@@ -56,6 +58,8 @@ import org.geysermc.geyser.impl.IdentifierImpl;
|
||||
import org.geysermc.geyser.impl.camera.GeyserCameraFade;
|
||||
import org.geysermc.geyser.impl.camera.GeyserCameraPosition;
|
||||
import org.geysermc.geyser.impl.entity.GeyserEntityDataImpl;
|
||||
import org.geysermc.geyser.impl.entity.GeyserListEntityDataImpl;
|
||||
import org.geysermc.geyser.impl.entity.HitboxImpl;
|
||||
import org.geysermc.geyser.item.GeyserCustomItemData;
|
||||
import org.geysermc.geyser.item.GeyserCustomItemOptions;
|
||||
import org.geysermc.geyser.item.GeyserNonVanillaCustomItemData;
|
||||
@@ -119,6 +123,9 @@ public class ProviderRegistryLoader implements RegistryLoader<Map<Class<?>, Prov
|
||||
providers.put(GeyserEntityDefinition.class, args -> BedrockEntityDefinition.getOrCreate((Identifier) args[0]));
|
||||
providers.put(JavaEntityType.class, args -> GeyserEntityType.ofVanilla((Identifier) args[0]));
|
||||
providers.put(GeyserEntityDataType.class, args -> GeyserEntityDataImpl.lookup((Class<?>) args[0], (String) args[1]));
|
||||
providers.put(GeyserListEntityDataType.class, args -> GeyserListEntityDataImpl.lookup((Class<?>) args[0], (Class<?>) args[1], (String) args[2]));
|
||||
|
||||
providers.put(Hitbox.Builder.class, args -> new HitboxImpl.Builder());
|
||||
|
||||
return providers;
|
||||
}
|
||||
|
||||
@@ -89,7 +89,6 @@ import java.util.function.Consumer;
|
||||
public final class EntityUtils {
|
||||
|
||||
private static final AtomicInteger RUNTIME_ID_ALLOCATOR = new AtomicInteger(100000);
|
||||
public static final float PLAYER_ENTITY_OFFSET = 1.62F;
|
||||
|
||||
/**
|
||||
* A constant array of the two hands that a player can interact with an entity.
|
||||
|
||||
Reference in New Issue
Block a user