1
0
mirror of https://github.com/GeyserMC/Geyser.git synced 2026-01-06 15:41:50 +00:00

API polishing; properly initialize height/width

This commit is contained in:
onebeastchris
2025-11-30 15:28:47 +01:00
parent a64865024a
commit a497d3d84c
17 changed files with 89 additions and 68 deletions

View File

@@ -26,10 +26,7 @@
package org.geysermc.geyser.entity;
import lombok.AccessLevel;
import lombok.Getter;
import lombok.Setter;
import lombok.ToString;
import lombok.experimental.Accessors;
import org.checkerframework.checker.nullness.qual.NonNull;
import org.checkerframework.checker.nullness.qual.Nullable;
import org.geysermc.geyser.api.entity.custom.CustomEntityDefinition;
@@ -42,17 +39,10 @@ import org.geysermc.geyser.registry.Registries;
import java.util.List;
import java.util.Objects;
@Getter
@Accessors(fluent = true)
@ToString
public class BedrockEntityDefinition implements GeyserEntityDefinition, CustomEntityDefinition {
private final @NonNull Identifier identifier;
private final @NonNull GeyserEntityProperties registeredProperties;
public BedrockEntityDefinition(@NonNull Identifier identifier, @NonNull GeyserEntityProperties registeredProperties) {
this.identifier = identifier;
this.registeredProperties = registeredProperties;
}
public record BedrockEntityDefinition(
@NonNull Identifier identifier,
@NonNull GeyserEntityProperties registeredProperties
) implements GeyserEntityDefinition, CustomEntityDefinition {
public static Builder builder() {
return new Builder();

View File

@@ -215,7 +215,7 @@ public record GeyserEntityType(Identifier identifier, int javaId) implements Jav
}
@Override
public Builder defaultBedrockDefinition(@Nullable GeyserEntityDefinition defaultBedrockDefinition) {
public Builder definition(@Nullable GeyserEntityDefinition defaultBedrockDefinition) {
if (defaultBedrockDefinition == null) {
this.defaultBedrockDefinition = null;
} else if (defaultBedrockDefinition instanceof BedrockEntityDefinition bedrockEntityDefinition) {

View File

@@ -38,12 +38,6 @@ import org.geysermc.mcprotocollib.protocol.data.game.entity.type.BuiltinEntityTy
@ToString(callSuper = true)
public class NonVanillaEntityTypeDefinition extends EntityTypeDefinition<Entity> {
/*
Soooooooooooooo this is fun.
- How should we expose entity metadata translators?
- Extending "vanilla" classes / entities??? Geyser uses instanceof checks...
*/
public NonVanillaEntityTypeDefinition(GeyserEntityType.Builder builder, GeyserEntityType entityType) {
super(Entity::new, entityType, builder.getWidth(), builder.getHeight(), 0f, builder.getDefaultBedrockDefinition(), VanillaEntityBases.ENTITY.translators);
}

View File

@@ -25,7 +25,6 @@
package org.geysermc.geyser.entity.spawn;
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.Setter;
import lombok.experimental.Accessors;
@@ -56,7 +55,6 @@ import java.util.function.Consumer;
@Getter
@Setter
@Accessors(fluent = true)
@AllArgsConstructor
public class EntitySpawnContext {
private final GeyserSession session;
private final EntityTypeDefinition<?> entityTypeDefinition;
@@ -80,29 +78,51 @@ public class EntitySpawnContext {
public EntitySpawnContext(GeyserSession session, EntityTypeDefinition<?> type, int javaId, UUID uuid) {
this(session, type, javaId, uuid, type.defaultBedrockDefinition(), Vector3f.ZERO, Vector3f.ZERO, 0, 0, 0,
type.height(), type.width(), type.offset(), null, null);
type.height(), type.width(), type.offset(), null);
}
public EntitySpawnContext(GeyserSession session, EntityTypeDefinition<?> type, int entityId, BedrockEntityDefinition definition, float height, float width, long geyserId) {
this(session, type, entityId, null, definition, Vector3f.ZERO, Vector3f.ZERO, 0, 0, 0, height, width, 0, geyserId, null);
this(session, type, entityId, null, definition, Vector3f.ZERO, Vector3f.ZERO, 0, 0, 0, height, width, 0, geyserId);
}
public static EntitySpawnContext fromPacket(GeyserSession session, EntityTypeDefinition<?> definition, ClientboundAddEntityPacket packet) {
Vector3f position = Vector3f.from(packet.getX(), packet.getY(), packet.getZ());
Vector3f motion = packet.getMovement().toFloat();
return new EntitySpawnContext(session, definition, packet.getEntityId(), packet.getUuid(), definition.defaultBedrockDefinition(),
position, motion, packet.getYaw(), packet.getPitch(), packet.getHeadYaw(), definition.height(), definition.width(), definition.offset(), null, null);
position, motion, packet.getYaw(), packet.getPitch(), packet.getHeadYaw(), definition.height(), definition.width(), definition.offset(), null);
}
public static EntitySpawnContext inherited(GeyserSession session, EntityTypeDefinition<?> definition, Entity base, Vector3f position) {
return new EntitySpawnContext(session, definition, 0, null, definition.defaultBedrockDefinition(), position, base.getMotion(), base.getYaw(),
base.getPitch(), base.getHeadYaw(), definition.height(), definition.width(), definition.offset(), null, null);
base.getPitch(), base.getHeadYaw(), definition.height(), definition.width(), definition.offset(), null);
}
public void callServerSpawnEvent() {
// TODO we should actually test this?
public EntitySpawnContext(GeyserSession session, EntityTypeDefinition<?> definition, int javaId, UUID uuid, BedrockEntityDefinition bedrockEntityDefinition, Vector3f position,
Vector3f motion, float yaw, float pitch, float headYaw, float height, float width, float offset, @Nullable Long geyserId) {
this.session = session;
this.entityTypeDefinition = definition;
this.javaId = javaId;
this.uuid = uuid;
this.bedrockEntityDefinition = bedrockEntityDefinition;
this.position = position;
this.motion = motion;
this.yaw = yaw;
this.pitch = pitch;
this.headYaw = headYaw;
this.height = height;
this.width = width;
this.offset = offset;
this.geyserId = geyserId;
this.consumers = null;
}
/**
* @return true if an entity should be spawned
*/
public boolean callServerSpawnEvent() {
// TODO add tests
if (EnvironmentUtils.IS_UNIT_TESTING) {
return;
return true;
}
GeyserImpl.getInstance().getEventBus().fire(new ServerSpawnEntityEvent(session) {
@@ -158,9 +178,11 @@ public class EntitySpawnContext {
consumers.add(consumer);
}
});
return bedrockEntityDefinition != null;
}
public void callParrotEvent(PlayerEntity player, int variant, boolean right) {
public boolean callParrotEvent(PlayerEntity player, int variant, boolean right) {
GeyserImpl.getInstance().eventBus().fire(new ServerAttachParrotsEvent(session) {
@Override
public GeyserPlayerEntity player() {
@@ -213,5 +235,7 @@ public class EntitySpawnContext {
consumers.add(consumer);
}
});
return bedrockEntityDefinition != null;
}
}

View File

@@ -55,7 +55,7 @@ public class DisplayBaseEntity extends Entity {
// On JE: custom name does not override text display.
}
public void setTranslation(EntityMetadata<Vector3f, ?> translationMeta){
public void setTranslation(EntityMetadata<Vector3f, ?> translationMeta) {
this.baseTranslation = translationMeta.getValue();
if (this.baseTranslation == null) {
return;

View File

@@ -108,6 +108,9 @@ public class Entity implements GeyserEntity {
@Setter(AccessLevel.NONE)
protected String nametag = "";
/**
* The entity position, WITH vertical offset
*/
protected Vector3f position;
protected Vector3f motion;
@@ -194,21 +197,18 @@ public class Entity implements GeyserEntity {
this.valid = false;
this.propertyManager = bedrockDefinition.registeredProperties().isEmpty() ? null : new GeyserEntityPropertyManager(bedrockDefinition.registeredProperties());
setPosition(context.position());
setPosition(context.position().up(offset));
setAirSupply(getMaxAir());
initializeMetadata();
// Allow API users to do things pre-spawn
if (context.consumers() != null) {
context.consumers().forEach(consumer -> consumer.accept(this));
}
}
/**
* Called on entity spawn. Used to populate the entity metadata and flags with default values.
*/
protected void initializeMetadata() {
dirtyMetadata.put(EntityDataTypes.WIDTH, width);
dirtyMetadata.put(EntityDataTypes.HEIGHT, height);
dirtyMetadata.put(EntityDataTypes.SCALE, 1f);
dirtyMetadata.put(EntityDataTypes.COLOR, (byte) 0);
dirtyMetadata.put(EntityDataTypes.AIR_SUPPLY_MAX, getMaxAir());
@@ -699,8 +699,6 @@ public class Entity implements GeyserEntity {
return this.valid;
}
/**
* Update the suggestion that the client currently has on their screen for this entity (for example, "Feed" or "Ride")
*/

View File

@@ -25,7 +25,6 @@
package org.geysermc.geyser.entity.type.living.monster;
import org.cloudburstmc.protocol.bedrock.data.entity.EntityDataTypes;
import org.cloudburstmc.protocol.bedrock.data.entity.EntityFlag;
import org.geysermc.geyser.entity.BedrockEntityDefinitions;
import org.geysermc.geyser.entity.VanillaEntities;
@@ -37,11 +36,13 @@ public class EnderDragonPartEntity extends Entity {
public EnderDragonPartEntity(GeyserSession session, int entityId, long geyserId, float width, float height) {
super(dragonPartSpawnContext(session, entityId, geyserId, width, height));
}
dirtyMetadata.put(EntityDataTypes.WIDTH, width);
dirtyMetadata.put(EntityDataTypes.HEIGHT, height);
@Override
protected void initializeMetadata() {
setFlag(EntityFlag.INVISIBLE, true);
setFlag(EntityFlag.FIRE_IMMUNE, true);
super.initializeMetadata();
}
public static EntitySpawnContext dragonPartSpawnContext(GeyserSession session, int entityId, long geyserId, float width, float height) {

View File

@@ -34,6 +34,7 @@ import org.cloudburstmc.protocol.bedrock.data.entity.EntityFlag;
import org.cloudburstmc.protocol.bedrock.data.entity.EntityLinkData;
import org.cloudburstmc.protocol.bedrock.packet.SetEntityLinkPacket;
import org.cloudburstmc.protocol.bedrock.packet.UpdateAttributesPacket;
import org.geysermc.geyser.GeyserImpl;
import org.geysermc.geyser.api.entity.type.player.GeyserPlayerEntity;
import org.geysermc.geyser.entity.VanillaEntities;
import org.geysermc.geyser.entity.attribute.GeyserAttributeType;
@@ -161,10 +162,16 @@ public class PlayerEntity extends AvatarEntity implements GeyserPlayerEntity {
}
// 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);
context.callParrotEvent(this, variant.getAsInt(), !isLeft);
if (context.callParrotEvent(this, variant.getAsInt(), !isLeft)) {
GeyserImpl.getInstance().getLogger().debug("TODO");
return;
}
ParrotEntity parrot = new ParrotEntity(context);
parrot.spawnEntity();
parrot.getDirtyMetadata().put(EntityDataTypes.VARIANT, variant.getAsInt());
if (context.consumers() != null) {
context.consumers().forEach(consumer -> consumer.accept(parrot));
}
parrot.spawnEntity();
// Different position whether the parrot is left or right
float offset = isLeft ? 0.4f : -0.4f;
parrot.getDirtyMetadata().put(EntityDataTypes.SEAT_OFFSET, Vector3f.from(offset, -0.22, -0.1));

View File

@@ -2403,7 +2403,7 @@ public class GeyserSession implements GeyserConnection, GeyserCommandSource {
}
@Override
public void requestHandSwap() {
public void switchHands() {
requestOffhandSwap();
}

View File

@@ -101,8 +101,7 @@ public class JavaAddEntityTranslator extends PacketTranslator<ClientboundAddEnti
return;
}
context.callServerSpawnEvent();
if (context.bedrockEntityDefinition() == null) {
if (!context.callServerSpawnEvent()) {
// TODO log warn
return;
}
@@ -136,6 +135,11 @@ public class JavaAddEntityTranslator extends PacketTranslator<ClientboundAddEnti
}
}
// Call pre-spawn consumer
if (context.consumers() != null) {
context.consumers().forEach(consumer -> consumer.accept(entity));
}
session.getEntityCache().spawnEntity(entity);
}
}

View File

@@ -35,6 +35,7 @@ import org.cloudburstmc.protocol.bedrock.data.GameType;
import org.cloudburstmc.protocol.bedrock.data.entity.EntityDataTypes;
import org.cloudburstmc.protocol.bedrock.data.entity.EntityFlag;
import org.geysermc.geyser.GeyserImpl;
import org.geysermc.geyser.api.entity.custom.CustomEntityDefinition;
import org.geysermc.geyser.api.entity.custom.CustomJavaEntityType;
import org.geysermc.geyser.api.entity.definition.GeyserEntityDefinition;
import org.geysermc.geyser.api.entity.property.GeyserEntityProperty;
@@ -430,13 +431,17 @@ public final class EntityUtils {
}
@Override
public void register(@NonNull GeyserEntityDefinition entityDefinition) {
public Collection<CustomEntityDefinition> customEntities() {
return Collections.unmodifiableCollection(customEntities);
}
@Override
public void register(@NonNull CustomEntityDefinition entityDefinition) {
Objects.requireNonNull(entityDefinition);
if (!(entityDefinition instanceof BedrockEntityDefinition bedrockEntityDefinition)) {
throw new IllegalArgumentException("EntityDefinition must not be a custom implementation of BedrockEntityDefinition! Found " + entityDefinition.getClass().getSimpleName());
}
if (Registries.BEDROCK_ENTITY_DEFINITIONS.get().containsValue(bedrockEntityDefinition)) {
if (entityDefinition.registered()) {
throw new IllegalStateException("Duplicate custom entity definition: " + entityDefinition);
}
if (bedrockEntityDefinition.vanilla()) {