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

Further refactor EntityDefinition, create a Base class

This commit is contained in:
Eclipse
2025-10-25 10:52:02 +02:00
committed by onebeastchris
parent 0b8678e2f2
commit d843745ded
10 changed files with 261 additions and 141 deletions

View File

@@ -42,6 +42,10 @@ public interface JavaEntityType {
return javaIdentifier().equals(javaIdentifier);
}
static JavaEntityType ofVanilla(@NonNull Identifier javaIdentifier) {
return GeyserApi.api().provider(JavaEntityType.class, javaIdentifier);
}
static JavaEntityType create(@NonNull Identifier javaIdentifier, @NonNegative int javaId) {
return GeyserApi.api().provider(JavaEntityType.class, javaIdentifier, javaId);
}

View File

@@ -25,14 +25,12 @@
package org.geysermc.geyser.entity;
import it.unimi.dsi.fastutil.objects.ObjectArrayList;
import lombok.AccessLevel;
import lombok.EqualsAndHashCode;
import lombok.Getter;
import lombok.Setter;
import lombok.ToString;
import lombok.experimental.Accessors;
import org.geysermc.geyser.GeyserImpl;
import org.geysermc.geyser.entity.factory.EntityFactory;
import org.geysermc.geyser.entity.properties.GeyserEntityProperties;
import org.geysermc.geyser.entity.properties.type.PropertyType;
@@ -53,120 +51,99 @@ import java.util.function.BiConsumer;
*/
@Getter
@Accessors(fluent = true)
@EqualsAndHashCode
@ToString
public class EntityDefinition<T extends Entity> {
@EqualsAndHashCode(callSuper = true)
@ToString(callSuper = true)
public abstract class EntityDefinition<T extends Entity> extends EntityDefinitionBase<T> {
private final EntityFactory<T> factory;
private final GeyserEntityType entityType;
private final String identifier;
private final float width;
private final float height;
private final float offset;
private final String bedrockIdentifier;
private final GeyserEntityProperties registeredProperties;
private final List<EntityMetadataTranslator<? super T, ?, ?>> translators;
/**
* @param identifier the Bedrock identifier of this entity
*/
public EntityDefinition(EntityFactory<T> factory, GeyserEntityType entityType, String identifier,
public EntityDefinition(EntityFactory<T> factory, GeyserEntityType entityType, String bedrockIdentifier,
float width, float height, float offset, GeyserEntityProperties registeredProperties, List<EntityMetadataTranslator<? super T, ?, ?>> translators) {
super(width, height, offset, translators);
this.factory = factory;
this.entityType = entityType;
this.identifier = identifier;
this.width = width;
this.height = height;
this.offset = offset;
this.bedrockIdentifier = bedrockIdentifier;
this.registeredProperties = registeredProperties;
this.translators = translators;
}
@SuppressWarnings("unchecked")
public <M> void translateMetadata(T entity, EntityMetadata<M, ? extends MetadataType<M>> metadata) {
EntityMetadataTranslator<? super T, M, EntityMetadata<M, ? extends MetadataType<M>>> translator = (EntityMetadataTranslator<? super T, M, EntityMetadata<M, ? extends MetadataType<M>>>) this.translators.get(metadata.getId());
if (translator == null) {
// This can safely happen; it means we don't translate this entity metadata
return;
}
if (translator.acceptedType() != metadata.getType()) {
GeyserImpl.getInstance().getLogger().warning("Metadata ID " + metadata.getId() + " was received with type " + metadata.getType() + " but we expected " + translator.acceptedType() + " for " + entity.getDefinition().entityType());
if (GeyserImpl.getInstance().config().debugMode()) {
GeyserImpl.getInstance().getLogger().debug(metadata.toString());
}
return;
}
translator.translate(entity, metadata);
}
@Setter
@Accessors(fluent = true, chain = true)
public static abstract class Builder<T extends Entity> {
public static abstract class Builder<T extends Entity> extends EntityDefinitionBase.Builder<T> {
protected final EntityFactory<T> factory;
@Setter(AccessLevel.NONE)
protected GeyserEntityType type;
protected String identifier;
protected float width;
protected float height;
protected float offset = 0.00001f;
protected String bedrockIdentifier;
@Setter(AccessLevel.NONE)
protected GeyserEntityProperties.Builder propertiesBuilder;
protected final List<EntityMetadataTranslator<? super T, ?, ?>> translators;
protected Builder(EntityFactory<T> factory) {
super();
this.factory = factory;
translators = new ObjectArrayList<>();
}
protected Builder(EntityFactory<T> factory, GeyserEntityType type, String identifier, float width, float height, float offset, List<EntityMetadataTranslator<? super T, ?, ?>> translators) {
protected Builder(EntityFactory<T> factory, float width, float height, float offset, List<EntityMetadataTranslator<? super T, ?, ?>> translators) {
super(width, height, offset, translators);
this.factory = factory;
this.type = type;
this.identifier = identifier;
this.width = width;
this.height = height;
this.offset = offset;
this.translators = translators;
}
/**
* Sets the height and width as one value
* Resets the bedrock identifier as well
*/
public Builder<T> heightAndWidth(float value) {
height = value;
width = value;
public Builder<T> type(GeyserEntityType type) {
this.type = type;
this.bedrockIdentifier = null;
return this;
}
@Override
public Builder<T> width(float width) {
return (Builder<T>) super.width(width);
}
@Override
public Builder<T> height(float height) {
return (Builder<T>) super.height(height);
}
@Override
public Builder<T> heightAndWidth(float value) {
return (Builder<T>) super.heightAndWidth(value);
}
@Override
public Builder<T> offset(float offset) {
this.offset = offset + 0.00001f;
return this;
return (Builder<T>) super.offset(offset);
}
@Override
public <U, EM extends EntityMetadata<U, ? extends MetadataType<U>>> Builder<T> addTranslator(MetadataType<U> type, BiConsumer<T, EM> translateFunction) {
return (Builder<T>) super.addTranslator(type, translateFunction);
}
@Override
public Builder<T> addTranslator(EntityMetadataTranslator<T, ?, ?> translator) {
return (Builder<T>) super.addTranslator(translator);
}
public Builder<T> property(PropertyType<?, ?> propertyType) {
if (this.propertiesBuilder == null) {
this.propertiesBuilder = new GeyserEntityProperties.Builder(this.identifier);
this.propertiesBuilder = new GeyserEntityProperties.Builder(this.bedrockIdentifier);
}
propertiesBuilder.add(propertyType);
return this;
}
public <U, EM extends EntityMetadata<U, ? extends MetadataType<U>>> Builder<T> addTranslator(MetadataType<U> type, BiConsumer<T, EM> translateFunction) {
translators.add(new EntityMetadataTranslator<>(type, translateFunction));
return this;
}
public Builder<T> addTranslator(EntityMetadataTranslator<T, ?, ?> translator) {
translators.add(translator);
return this;
}
public EntityDefinition<T> build() {
if (identifier == null && type != null) {
identifier = type.javaIdentifier().toString();
protected void validateTypeAndIdentifier() {
if (type == null) {
throw new IllegalStateException("Missing entity type!");
} else if (bedrockIdentifier == null) {
bedrockIdentifier = type.javaIdentifier().toString();
}
GeyserEntityProperties registeredProperties = propertiesBuilder == null ? null : propertiesBuilder.build();
return new EntityDefinition<>(factory, type, identifier, width, height, offset, registeredProperties, translators);
}
}
}

View File

@@ -0,0 +1,139 @@
/*
* 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.entity;
import it.unimi.dsi.fastutil.objects.ObjectArrayList;
import lombok.EqualsAndHashCode;
import lombok.Getter;
import lombok.Setter;
import lombok.ToString;
import lombok.experimental.Accessors;
import org.geysermc.geyser.GeyserImpl;
import org.geysermc.geyser.entity.type.Entity;
import org.geysermc.geyser.translator.entity.EntityMetadataTranslator;
import org.geysermc.mcprotocollib.protocol.data.game.entity.metadata.EntityMetadata;
import org.geysermc.mcprotocollib.protocol.data.game.entity.metadata.MetadataType;
import java.util.List;
import java.util.function.BiConsumer;
@Getter
@Accessors(fluent = true)
@EqualsAndHashCode
@ToString
public class EntityDefinitionBase<T extends Entity> {
private final float width;
private final float height;
private final float offset;
private final List<EntityMetadataTranslator<? super T, ?, ?>> translators;
public EntityDefinitionBase(float width, float height, float offset, List<EntityMetadataTranslator<? super T, ?, ?>> translators) {
this.width = width;
this.height = height;
this.offset = offset;
this.translators = translators;
}
public static <T extends Entity> Builder<T> baseBuilder(Class<T> clazz) {
return new Builder<>(clazz);
}
public static <T extends Entity> Builder<T> baseInherited(EntityDefinitionBase<? super T> parent) {
return new Builder<>(parent.width(), parent.height(), parent.offset(), new ObjectArrayList<>(parent.translators()));
}
@SuppressWarnings("unchecked")
public <M> void translateMetadata(T entity, EntityMetadata<M, ? extends MetadataType<M>> metadata) {
EntityMetadataTranslator<? super T, M, EntityMetadata<M, ? extends MetadataType<M>>> translator = (EntityMetadataTranslator<? super T, M, EntityMetadata<M, ? extends MetadataType<M>>>) this.translators.get(metadata.getId());
if (translator == null) {
// This can safely happen; it means we don't translate this entity metadata
return;
}
if (translator.acceptedType() != metadata.getType()) {
GeyserImpl.getInstance().getLogger().warning("Metadata ID " + metadata.getId() + " was received with type " + metadata.getType() + " but we expected " + translator.acceptedType() + " for " + entity.getDefinition().entityType());
if (GeyserImpl.getInstance().getConfig().isDebugMode()) {
GeyserImpl.getInstance().getLogger().debug(metadata.toString());
}
return;
}
translator.translate(entity, metadata);
}
@Setter
@Accessors(fluent = true, chain = true)
public static class Builder<T extends Entity> {
protected float width;
protected float height;
protected float offset = 0.00001f;
protected final List<EntityMetadataTranslator<? super T, ?, ?>> translators;
protected Builder() {
translators = new ObjectArrayList<>();
}
// Unused param so Java knows what entity we're talking about
protected Builder(@SuppressWarnings("unused") Class<T> clazz) {
this();
}
protected Builder(float width, float height, float offset, List<EntityMetadataTranslator<? super T, ?, ?>> translators) {
this.width = width;
this.height = height;
this.offset = offset;
this.translators = translators;
}
/**
* Sets the height and width as one value
*/
public Builder<T> heightAndWidth(float value) {
height = value;
width = value;
return this;
}
public Builder<T> offset(float offset) {
this.offset = offset + 0.00001f;
return this;
}
public <U, EM extends EntityMetadata<U, ? extends MetadataType<U>>> Builder<T> addTranslator(MetadataType<U> type, BiConsumer<T, EM> translateFunction) {
translators.add(new EntityMetadataTranslator<>(type, translateFunction));
return this;
}
public Builder<T> addTranslator(EntityMetadataTranslator<T, ?, ?> translator) {
translators.add(translator);
return this;
}
public EntityDefinitionBase<T> build() {
return new EntityDefinitionBase<>(width, height, offset, translators);
}
}
}

View File

@@ -364,7 +364,7 @@ public final class EntityDefinitions {
END_CRYSTAL = VanillaEntityDefinition.inherited(EnderCrystalEntity::new, entityBase)
.type(BuiltinEntityType.END_CRYSTAL)
.heightAndWidth(2.0f)
.identifier("minecraft:ender_crystal")
.bedrockIdentifier("minecraft:ender_crystal")
.addTranslator(MetadataTypes.OPTIONAL_BLOCK_POS, EnderCrystalEntity::setBlockTarget)
.addTranslator(MetadataTypes.BOOLEAN,
(enderCrystalEntity, entityMetadata) -> enderCrystalEntity.setFlag(EntityFlag.SHOW_BOTTOM, ((BooleanEntityMetadata) entityMetadata).getPrimitiveValue())) // There is a base located on the ender crystal
@@ -372,17 +372,17 @@ public final class EntityDefinitions {
EXPERIENCE_ORB = VanillaEntityDefinition.inherited(ExpOrbEntity::new, entityBase)
.type(BuiltinEntityType.EXPERIENCE_ORB)
.addTranslator(null) // int determining xb orb texture
.identifier("minecraft:xp_orb")
.bedrockIdentifier("minecraft:xp_orb")
.build();
EVOKER_FANGS = VanillaEntityDefinition.inherited(EvokerFangsEntity::new, entityBase)
.type(BuiltinEntityType.EVOKER_FANGS)
.height(0.8f).width(0.5f)
.identifier("minecraft:evocation_fang")
.bedrockIdentifier("minecraft:evocation_fang")
.build();
EYE_OF_ENDER = VanillaEntityDefinition.inherited(EnderEyeEntity::new, entityBase)
.type(BuiltinEntityType.EYE_OF_ENDER)
.heightAndWidth(0.25f)
.identifier("minecraft:eye_of_ender_signal")
.bedrockIdentifier("minecraft:eye_of_ender_signal")
.addTranslator(null) // Item
.build();
FALLING_BLOCK = VanillaEntityDefinition.<FallingBlockEntity>inherited(null, entityBase)
@@ -393,14 +393,14 @@ public final class EntityDefinitions {
FIREWORK_ROCKET = VanillaEntityDefinition.inherited(FireworkEntity::new, entityBase)
.type(BuiltinEntityType.FIREWORK_ROCKET)
.heightAndWidth(0.25f)
.identifier("minecraft:fireworks_rocket")
.bedrockIdentifier("minecraft:fireworks_rocket")
.addTranslator(MetadataTypes.ITEM_STACK, FireworkEntity::setFireworkItem)
.addTranslator(MetadataTypes.OPTIONAL_UNSIGNED_INT, FireworkEntity::setPlayerGliding)
.addTranslator(null) // Shot at angle
.build();
FISHING_BOBBER = VanillaEntityDefinition.<FishingHookEntity>inherited(null, entityBase)
.type(BuiltinEntityType.FISHING_BOBBER)
.identifier("minecraft:fishing_hook")
.bedrockIdentifier("minecraft:fishing_hook")
.addTranslator(MetadataTypes.INT, FishingHookEntity::setHookedEntity)
.addTranslator(null) // Biting TODO check
.build();
@@ -451,7 +451,7 @@ public final class EntityDefinitions {
.build();
TEXT_DISPLAY = VanillaEntityDefinition.inherited(TextDisplayEntity::new, displayBase)
.type(BuiltinEntityType.TEXT_DISPLAY)
.identifier("minecraft:armor_stand")
.bedrockIdentifier("minecraft:armor_stand")
.offset(-0.5f)
.addTranslator(MetadataTypes.COMPONENT, TextDisplayEntity::setText)
.addTranslator(null) // Line width
@@ -463,7 +463,7 @@ public final class EntityDefinitions {
INTERACTION = VanillaEntityDefinition.inherited(InteractionEntity::new, entityBase)
.type(BuiltinEntityType.INTERACTION)
.heightAndWidth(1.0f) // default size until server specifies otherwise
.identifier("minecraft:armor_stand")
.bedrockIdentifier("minecraft:armor_stand")
.addTranslator(MetadataTypes.FLOAT, InteractionEntity::setWidth)
.addTranslator(MetadataTypes.FLOAT, InteractionEntity::setHeight)
.addTranslator(MetadataTypes.BOOLEAN, InteractionEntity::setResponse)
@@ -496,17 +496,17 @@ public final class EntityDefinitions {
EXPERIENCE_BOTTLE = VanillaEntityDefinition.inherited(ThrowableItemEntity::new, throwableItemBase)
.type(BuiltinEntityType.EXPERIENCE_BOTTLE)
.heightAndWidth(0.25f)
.identifier("minecraft:xp_bottle")
.bedrockIdentifier("minecraft:xp_bottle")
.build();
SPLASH_POTION = VanillaEntityDefinition.inherited(ThrownPotionEntity::new, throwableItemBase)
.type(BuiltinEntityType.SPLASH_POTION)
.heightAndWidth(0.25f)
.identifier("minecraft:splash_potion")
.bedrockIdentifier("minecraft:splash_potion")
.build();
LINGERING_POTION = VanillaEntityDefinition.inherited(ThrownPotionEntity::new, throwableItemBase)
.type(BuiltinEntityType.LINGERING_POTION)
.heightAndWidth(0.25f)
.identifier("minecraft:splash_potion")
.bedrockIdentifier("minecraft:splash_potion")
.build();
SNOWBALL = VanillaEntityDefinition.inherited(ThrowableItemEntity::new, throwableItemBase)
.type(BuiltinEntityType.SNOWBALL)
@@ -516,12 +516,12 @@ public final class EntityDefinitions {
EntityFactory<AbstractWindChargeEntity> windChargeSupplier = AbstractWindChargeEntity::new;
BREEZE_WIND_CHARGE = VanillaEntityDefinition.inherited(windChargeSupplier, entityBase)
.type(BuiltinEntityType.BREEZE_WIND_CHARGE)
.identifier("minecraft:breeze_wind_charge_projectile")
.bedrockIdentifier("minecraft:breeze_wind_charge_projectile")
.heightAndWidth(0.3125f)
.build();
WIND_CHARGE = VanillaEntityDefinition.inherited(windChargeSupplier, entityBase)
.type(BuiltinEntityType.WIND_CHARGE)
.identifier("minecraft:wind_charge_projectile")
.bedrockIdentifier("minecraft:wind_charge_projectile")
.heightAndWidth(0.3125f)
.build();
@@ -538,11 +538,11 @@ public final class EntityDefinitions {
SPECTRAL_ARROW = VanillaEntityDefinition.inherited(abstractArrowBase.factory(), abstractArrowBase)
.type(BuiltinEntityType.SPECTRAL_ARROW)
.heightAndWidth(0.25f)
.identifier("minecraft:arrow")
.bedrockIdentifier("minecraft:arrow")
.build();
TRIDENT = VanillaEntityDefinition.inherited(TridentEntity::new, abstractArrowBase) // TODO remove class
.type(BuiltinEntityType.TRIDENT)
.identifier("minecraft:thrown_trident")
.bedrockIdentifier("minecraft:thrown_trident")
.addTranslator(null) // Loyalty
.addTranslator(MetadataTypes.BOOLEAN, (tridentEntity, entityMetadata) -> tridentEntity.setFlag(EntityFlag.ENCHANTED, ((BooleanEntityMetadata) entityMetadata).getPrimitiveValue()))
.build();
@@ -588,7 +588,7 @@ public final class EntityDefinitions {
.build();
FURNACE_MINECART = VanillaEntityDefinition.inherited(FurnaceMinecartEntity::new, MINECART)
.type(BuiltinEntityType.FURNACE_MINECART)
.identifier("minecraft:minecart")
.bedrockIdentifier("minecraft:minecart")
.addTranslator(MetadataTypes.BOOLEAN, FurnaceMinecartEntity::setHasFuel)
.build();
HOPPER_MINECART = VanillaEntityDefinition.inherited(MINECART.factory(), MINECART)
@@ -596,7 +596,7 @@ public final class EntityDefinitions {
.build();
SPAWNER_MINECART = VanillaEntityDefinition.inherited(SpawnerMinecartEntity::new, MINECART)
.type(BuiltinEntityType.SPAWNER_MINECART)
.identifier("minecraft:minecart")
.bedrockIdentifier("minecraft:minecart")
.build();
TNT_MINECART = VanillaEntityDefinition.inherited(MINECART.factory(), MINECART)
.type(BuiltinEntityType.TNT_MINECART)
@@ -779,7 +779,7 @@ public final class EntityDefinitions {
.type(BuiltinEntityType.GIANT)
.height(1.8f).width(1.6f)
.offset(1.62f)
.identifier("minecraft:zombie")
.bedrockIdentifier("minecraft:zombie")
.build();
IRON_GOLEM = VanillaEntityDefinition.inherited(IronGolemEntity::new, mobEntityBase)
.type(BuiltinEntityType.IRON_GOLEM)
@@ -868,7 +868,7 @@ public final class EntityDefinitions {
.type(BuiltinEntityType.ZOMBIE_VILLAGER)
.height(1.8f).width(0.6f)
.offset(1.62f)
.identifier("minecraft:zombie_villager_v2")
.bedrockIdentifier("minecraft:zombie_villager_v2")
.addTranslator(MetadataTypes.BOOLEAN, ZombieVillagerEntity::setTransforming)
.addTranslator(MetadataTypes.VILLAGER_DATA, ZombieVillagerEntity::setZombieVillagerData)
.build();
@@ -876,7 +876,7 @@ public final class EntityDefinitions {
.type(BuiltinEntityType.ZOMBIFIED_PIGLIN)
.height(1.95f).width(0.6f)
.offset(1.62f)
.identifier("minecraft:zombie_pigman")
.bedrockIdentifier("minecraft:zombie_pigman")
.build();
DROWNED = VanillaEntityDefinition.inherited(ZOMBIE.factory(), ZOMBIE)
@@ -931,7 +931,7 @@ public final class EntityDefinitions {
TROPICAL_FISH = VanillaEntityDefinition.inherited(TropicalFishEntity::new, abstractFishEntityBase)
.type(BuiltinEntityType.TROPICAL_FISH)
.heightAndWidth(0.6f)
.identifier("minecraft:tropicalfish")
.bedrockIdentifier("minecraft:tropicalfish")
.addTranslator(MetadataTypes.INT, TropicalFishEntity::setFishVariant)
.build();
@@ -959,12 +959,12 @@ public final class EntityDefinitions {
EVOKER = VanillaEntityDefinition.inherited(spellcasterEntityBase.factory(), spellcasterEntityBase)
.type(BuiltinEntityType.EVOKER)
.height(1.95f).width(0.6f)
.identifier("minecraft:evocation_illager")
.bedrockIdentifier("minecraft:evocation_illager")
.build();
ILLUSIONER = VanillaEntityDefinition.inherited(spellcasterEntityBase.factory(), spellcasterEntityBase)
.type(BuiltinEntityType.ILLUSIONER)
.height(1.95f).width(0.6f)
.identifier("minecraft:evocation_illager")
.bedrockIdentifier("minecraft:evocation_illager")
.build();
PILLAGER = VanillaEntityDefinition.inherited(PillagerEntity::new, raidParticipantEntityBase)
.type(BuiltinEntityType.PILLAGER)
@@ -1132,7 +1132,7 @@ public final class EntityDefinitions {
.type(BuiltinEntityType.VILLAGER)
.height(1.8f).width(0.6f)
.offset(1.62f)
.identifier("minecraft:villager_v2")
.bedrockIdentifier("minecraft:villager_v2")
.addTranslator(MetadataTypes.VILLAGER_DATA, VillagerEntity::setVillagerData)
.build();
WANDERING_TRADER = VanillaEntityDefinition.inherited(abstractVillagerEntityBase.factory(), abstractVillagerEntityBase)
@@ -1205,7 +1205,7 @@ public final class EntityDefinitions {
.build();
TRADER_LLAMA = VanillaEntityDefinition.inherited(TraderLlamaEntity::new, LLAMA)
.type(BuiltinEntityType.TRADER_LLAMA)
.identifier("minecraft:llama")
.bedrockIdentifier("minecraft:llama")
.build();
}
@@ -1240,7 +1240,7 @@ public final class EntityDefinitions {
// As of 1.18 these don't track entity data at all
ENDER_DRAGON_PART = VanillaEntityDefinition.<EnderDragonPartEntity>builder(null)
.identifier("minecraft:armor_stand") // Emulated
.bedrockIdentifier("minecraft:armor_stand") // Emulated
.build(false); // Never sent over the network
Registries.JAVA_ENTITY_IDENTIFIERS.get().put("minecraft:marker", null); // We don't need an entity definition for this as it is never sent over the network
@@ -1250,7 +1250,7 @@ public final class EntityDefinitions {
return VanillaEntityDefinition.inherited((session, javaId, bedrockId, uuid, definition, position, motion, yaw, pitch, headYaw) ->
new BoatEntity(session, javaId, bedrockId, uuid, definition, position, motion, yaw, variant), base)
.type(BuiltinEntityType)
.identifier("minecraft:boat")
.bedrockIdentifier("minecraft:boat")
.build();
}
@@ -1258,7 +1258,7 @@ public final class EntityDefinitions {
return VanillaEntityDefinition.inherited((session, javaId, bedrockId, uuid, definition, position, motion, yaw, pitch, headYaw) ->
new ChestBoatEntity(session, javaId, bedrockId, uuid, definition, position, motion, yaw, variant), base)
.type(BuiltinEntityType)
.identifier("minecraft:chest_boat")
.bedrockIdentifier("minecraft:chest_boat")
.build();
}
@@ -1339,7 +1339,7 @@ public final class EntityDefinitions {
});
for (var definition : Registries.ENTITY_DEFINITIONS.get().values()) {
if (!definition.registeredProperties().isEmpty()) {
if (!definition.registeredProperties().isEmpty()) { // TODO Null or empty check??
Registries.BEDROCK_ENTITY_PROPERTIES.get().add(definition.registeredProperties().toNbtMap(definition.identifier()));
}
}

View File

@@ -36,6 +36,7 @@ import org.checkerframework.checker.nullness.qual.Nullable;
import org.geysermc.geyser.Constants;
import org.geysermc.geyser.api.entity.JavaEntityType;
import org.geysermc.geyser.api.util.Identifier;
import org.geysermc.geyser.impl.IdentifierImpl;
import org.geysermc.geyser.util.MinecraftKey;
import org.geysermc.mcprotocollib.protocol.data.game.entity.type.BuiltinEntityType;
import org.geysermc.mcprotocollib.protocol.data.game.entity.type.EntityType;
@@ -46,21 +47,29 @@ import java.util.Map;
import java.util.Objects;
public record GeyserEntityType(Identifier javaIdentifier, int javaId) implements JavaEntityType {
private static final Identifier UNREGISTERED = IdentifierImpl.of(Constants.GEYSER_CUSTOM_NAMESPACE, "unregistered_sadface");
private static final Map<BuiltinEntityType, GeyserEntityType> VANILLA = new EnumMap<>(BuiltinEntityType.class);
private static final Int2ObjectMap<GeyserEntityType> CUSTOM = new Int2ObjectOpenHashMap<>();
private static final Object2ObjectMap<Identifier, GeyserEntityType> CUSTOM_BY_IDENTIFIER = new Object2ObjectOpenHashMap<>();
public GeyserEntityType {
if (!VANILLA.containsValue(this) && !CUSTOM.containsKey(javaId)) {
throw new IllegalCallerException("Public constructor of GeyserEntityType should not be used; use one of the static factory methods instead");
}
}
private GeyserEntityType(BuiltinEntityType builtin) {
this(Identifier.of(builtin.name().toLowerCase(Locale.ROOT)), builtin.id());
}
private GeyserEntityType(int javaId) {
this(Identifier.of(Constants.GEYSER_CUSTOM_NAMESPACE, "unregistered_sadface"), javaId);
this(UNREGISTERED, javaId);
}
@Override
public boolean isUnregistered() {
return javaIdentifier.namespace().equals(Constants.GEYSER_CUSTOM_NAMESPACE) && javaIdentifier.path().equals("unregistered_sadface");
return javaIdentifier.equals(UNREGISTERED);
}
public boolean is(EntityType type) {
@@ -71,6 +80,13 @@ public record GeyserEntityType(Identifier javaIdentifier, int javaId) implements
return VANILLA.computeIfAbsent(builtin, GeyserEntityType::new);
}
/**
* @throws IllegalArgumentException document this in API
*/
public static GeyserEntityType ofVanilla(Identifier javaIdentifier) {
return ofVanilla(BuiltinEntityType.valueOf(javaIdentifier.path().toUpperCase(Locale.ROOT)));
}
public static GeyserEntityType of(int javaId) {
if (javaId >= 0 && javaId < BuiltinEntityType.VALUES.length) {
return ofVanilla(BuiltinEntityType.VALUES[javaId]);
@@ -84,7 +100,7 @@ public record GeyserEntityType(Identifier javaIdentifier, int javaId) implements
public static GeyserEntityType of(Key javaKey) {
if (javaKey.namespace().equals(Key.MINECRAFT_NAMESPACE)) {
try {
return ofVanilla(BuiltinEntityType.valueOf(javaKey.value().toUpperCase(Locale.ROOT)));
return ofVanilla(MinecraftKey.keyToIdentifier(javaKey));
} catch (IllegalArgumentException exception) {
return null;
}

View File

@@ -48,50 +48,37 @@ import java.util.function.BiConsumer;
@EqualsAndHashCode(callSuper = true)
@ToString(callSuper = true)
public class VanillaEntityDefinition<T extends Entity> extends EntityDefinition<T> {
private final BuiltinEntityType builtinType;
/**
* @param identifier the Bedrock identifier of this entity
*/
public VanillaEntityDefinition(EntityFactory<T> factory, GeyserEntityType entityType, String identifier,
float width, float height, float offset, GeyserEntityProperties registeredProperties, List<EntityMetadataTranslator<? super T, ?, ?>> translators,
BuiltinEntityType builtinType) {
super(factory, entityType, identifier, width, height, offset, registeredProperties, translators);
this.builtinType = builtinType;
}
public static <T extends Entity> Builder<T> inherited(EntityFactory<T> factory, EntityDefinition<? super T> parent) {
return new Builder<>(factory, parent.entityType(), parent.identifier(), parent.width(), parent.height(), parent.offset(), new ObjectArrayList<>(parent.translators()));
public VanillaEntityDefinition(EntityFactory<T> factory, GeyserEntityType entityType, String bedrockIdentifier,
float width, float height, float offset, GeyserEntityProperties registeredProperties, List<EntityMetadataTranslator<? super T, ?, ?>> translators) {
super(factory, entityType, bedrockIdentifier, width, height, offset, registeredProperties, translators);
}
public static <T extends Entity> Builder<T> builder(EntityFactory<T> factory) {
return new Builder<>(factory);
}
public static class Builder<T extends Entity> extends EntityDefinition.Builder<T> {
private BuiltinEntityType builtinType;
public static <T extends Entity> Builder<T> inherited(EntityFactory<T> factory, EntityDefinition<? super T> parent) {
return new Builder<>(factory, parent.width(), parent.height(), parent.offset(), new ObjectArrayList<>(parent.translators()));
}
private Builder(EntityFactory<T> factory) {
public static class Builder<T extends Entity> extends EntityDefinition.Builder<T> {
protected Builder(EntityFactory<T> factory) {
super(factory);
}
public Builder(EntityFactory<T> factory, GeyserEntityType type, String identifier, float width, float height, float offset, List<EntityMetadataTranslator<? super T, ?, ?>> entityMetadataTranslators) {
super(factory, type, identifier, width, height, offset, entityMetadataTranslators);
protected Builder(EntityFactory<T> factory, float width, float height, float offset, List<EntityMetadataTranslator<? super T, ?, ?>> translators) {
super(factory, width, height, offset, translators);
}
/**
* Resets the identifier as well
*/
public Builder<T> type(BuiltinEntityType type) {
this.type = GeyserEntityType.ofVanilla(type);
builtinType = type;
identifier(null);
return this;
return (Builder<T>) super.type(GeyserEntityType.ofVanilla(type));
}
@Override
public Builder<T> identifier(String identifier) {
return (Builder<T>) super.identifier(identifier);
public Builder<T> bedrockIdentifier(String bedrockIdentifier) {
return (Builder<T>) super.bedrockIdentifier(bedrockIdentifier);
}
@Override
@@ -138,13 +125,11 @@ public class VanillaEntityDefinition<T extends Entity> extends EntityDefinition<
* @param register whether to register this entity in the Registries for entity types. Generally this should be
* set to false if we're not expecting this entity to spawn from the network.
*/
// TODO fix code duplication
public VanillaEntityDefinition<T> build(boolean register) {
if (identifier == null && type != null) {
identifier = type.javaIdentifier().toString();
}
validateTypeAndIdentifier();
GeyserEntityProperties registeredProperties = propertiesBuilder == null ? null : propertiesBuilder.build();
VanillaEntityDefinition<T> definition = new VanillaEntityDefinition<>(factory, type, identifier, width, height, offset, registeredProperties, translators, builtinType);
VanillaEntityDefinition<T> definition = new VanillaEntityDefinition<>(factory, type, bedrockIdentifier, width, height, offset, registeredProperties, translators);
if (register && definition.entityType() != null) {
Registries.ENTITY_DEFINITIONS.get().putIfAbsent(definition.entityType(), definition);
Registries.JAVA_ENTITY_IDENTIFIERS.get().putIfAbsent(type.javaIdentifier().toString(), definition);

View File

@@ -70,7 +70,6 @@ import org.geysermc.mcprotocollib.protocol.data.game.entity.metadata.type.Boolea
import org.geysermc.mcprotocollib.protocol.data.game.entity.metadata.type.ByteEntityMetadata;
import org.geysermc.mcprotocollib.protocol.data.game.entity.metadata.type.IntEntityMetadata;
import org.geysermc.mcprotocollib.protocol.data.game.entity.player.Hand;
import org.geysermc.mcprotocollib.protocol.data.game.entity.type.EntityType;
import java.util.Collections;
import java.util.EnumMap;
@@ -199,7 +198,7 @@ public class Entity implements GeyserEntity {
public void spawnEntity() {
AddEntityPacket addEntityPacket = new AddEntityPacket();
addEntityPacket.setIdentifier(definition.identifier());
addEntityPacket.setIdentifier(definition.bedrockIdentifier());
addEntityPacket.setRuntimeEntityId(geyserId);
addEntityPacket.setUniqueEntityId(geyserId);
addEntityPacket.setPosition(position);

View File

@@ -112,7 +112,7 @@ public class ProviderRegistryLoader implements RegistryLoader<Map<Class<?>, Prov
providers.put(CameraPosition.Builder.class, args -> new GeyserCameraPosition.Builder());
// entities
providers.put(JavaEntityType.class, args -> GeyserEntityType.createCustom((Identifier) args[0], (int) args[1]));
providers.put(JavaEntityType.class, args -> args.length == 1 ? GeyserEntityType.ofVanilla((Identifier) args[0]) : GeyserEntityType.createCustom((Identifier) args[0], (int) args[1]));
return providers;
}

View File

@@ -49,7 +49,7 @@ public class TrialSpawnerBlockEntityTranslator extends BlockEntityTranslator {
NbtMapBuilder spawnData = NbtMap.builder();
EntityDefinition<?> definition = Registries.JAVA_ENTITY_IDENTIFIERS.get(entityData.getString("id"));
if (definition != null) {
spawnData.putString("TypeId", definition.identifier());
spawnData.putString("TypeId", definition.bedrockIdentifier());
}
spawnData.putInt("Weight", entityData.getInt("Size", 1)); // ??? presumably since these are the only other two extra attributes
bedrockNbt.putCompound("spawn_data", spawnData.build());

View File

@@ -50,7 +50,7 @@ public class JavaSetEquipmentTranslator extends PacketTranslator<ClientboundSetE
if (!(entity instanceof LivingEntity livingEntity)) {
session.getGeyser().getLogger().debug("Attempted to add armor to a non-living entity (" +
entity.getDefinition().identifier() + ").");
entity.getDefinition().bedrockIdentifier() + ").");
return;
}