From 35ef08e5bb86a87008d1cc6035f619e4d66a44d3 Mon Sep 17 00:00:00 2001 From: basaigh <53559772+basaigh@users.noreply.github.com> Date: Thu, 12 Jun 2025 23:31:54 +0100 Subject: [PATCH] Initial happy ghast implementation --- .../geyser/entity/EntityDefinitions.java | 9 ++ .../properties/VanillaEntityProperties.java | 4 + .../type/living/animal/HappyGhastEntity.java | 126 ++++++++++++++++++ .../geyser/network/UpstreamPacketHandler.java | 2 + .../geyser/session/GeyserSession.java | 2 + .../geysermc/geyser/util/InteractiveTag.java | 3 +- .../resources/bedrock/entity_identifiers.dat | Bin 8378 -> 8445 bytes 7 files changed, 145 insertions(+), 1 deletion(-) create mode 100644 core/src/main/java/org/geysermc/geyser/entity/type/living/animal/HappyGhastEntity.java diff --git a/core/src/main/java/org/geysermc/geyser/entity/EntityDefinitions.java b/core/src/main/java/org/geysermc/geyser/entity/EntityDefinitions.java index 77030306c..09966f24a 100644 --- a/core/src/main/java/org/geysermc/geyser/entity/EntityDefinitions.java +++ b/core/src/main/java/org/geysermc/geyser/entity/EntityDefinitions.java @@ -81,6 +81,7 @@ import org.geysermc.geyser.entity.type.living.TadpoleEntity; import org.geysermc.geyser.entity.type.living.animal.ArmadilloEntity; import org.geysermc.geyser.entity.type.living.animal.AxolotlEntity; import org.geysermc.geyser.entity.type.living.animal.BeeEntity; +import org.geysermc.geyser.entity.type.living.animal.HappyGhastEntity; import org.geysermc.geyser.entity.type.living.animal.farm.ChickenEntity; import org.geysermc.geyser.entity.type.living.animal.farm.CowEntity; import org.geysermc.geyser.entity.type.living.animal.FoxEntity; @@ -215,6 +216,7 @@ public final class EntityDefinitions { public static final EntityDefinition GLOW_SQUID; public static final EntityDefinition GOAT; public static final EntityDefinition GUARDIAN; + public static final EntityDefinition HAPPY_GHAST; public static final EntityDefinition HOGLIN; public static final EntityDefinition HOPPER_MINECART; public static final EntityDefinition HORSE; @@ -989,6 +991,13 @@ public final class EntityDefinitions { .addTranslator(MetadataTypes.FROG_VARIANT, FrogEntity::setVariant) .addTranslator(MetadataTypes.OPTIONAL_UNSIGNED_INT, FrogEntity::setTongueTarget) .build(); + HAPPY_GHAST = EntityDefinition.inherited(HappyGhastEntity::new, ageableEntityBase) + .type(EntityType.HAPPY_GHAST) + .heightAndWidth(4f) + .properties(VanillaEntityProperties.HAPPY_GHAST) + .addTranslator(null) // Is leash holder + .addTranslator(MetadataTypes.BOOLEAN, HappyGhastEntity::setStaysStill) + .build(); HOGLIN = EntityDefinition.inherited(HoglinEntity::new, ageableEntityBase) .type(EntityType.HOGLIN) .height(1.4f).width(1.3965f) diff --git a/core/src/main/java/org/geysermc/geyser/entity/properties/VanillaEntityProperties.java b/core/src/main/java/org/geysermc/geyser/entity/properties/VanillaEntityProperties.java index 305dbf22e..06a715f18 100644 --- a/core/src/main/java/org/geysermc/geyser/entity/properties/VanillaEntityProperties.java +++ b/core/src/main/java/org/geysermc/geyser/entity/properties/VanillaEntityProperties.java @@ -61,6 +61,10 @@ public class VanillaEntityProperties { .addInt(CreakingEntity.CREAKING_SWAYING_TICKS, 0, 6) .build(); + public static final GeyserEntityProperties HAPPY_GHAST = new GeyserEntityProperties.Builder() + .addBoolean("minecraft:can_move") + .build(); + public static final GeyserEntityProperties WOLF_SOUND_VARIANT = new GeyserEntityProperties.Builder() .addEnum("minecraft:sound_variant", "default", diff --git a/core/src/main/java/org/geysermc/geyser/entity/type/living/animal/HappyGhastEntity.java b/core/src/main/java/org/geysermc/geyser/entity/type/living/animal/HappyGhastEntity.java new file mode 100644 index 000000000..e5cec753f --- /dev/null +++ b/core/src/main/java/org/geysermc/geyser/entity/type/living/animal/HappyGhastEntity.java @@ -0,0 +1,126 @@ +/* + * 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.type.living.animal; + +import org.checkerframework.checker.nullness.qual.NonNull; +import org.checkerframework.checker.nullness.qual.Nullable; +import org.cloudburstmc.math.vector.Vector3f; +import org.cloudburstmc.protocol.bedrock.data.entity.EntityFlag; +import org.geysermc.geyser.entity.EntityDefinition; +import org.geysermc.geyser.inventory.GeyserItemStack; +import org.geysermc.geyser.item.type.Item; +import org.geysermc.geyser.session.GeyserSession; +import org.geysermc.geyser.session.cache.tags.ItemTag; +import org.geysermc.geyser.session.cache.tags.Tag; +import org.geysermc.geyser.util.InteractionResult; +import org.geysermc.geyser.util.InteractiveTag; +import org.geysermc.mcprotocollib.protocol.data.game.entity.metadata.type.BooleanEntityMetadata; +import org.geysermc.mcprotocollib.protocol.data.game.entity.player.Hand; + +import java.util.UUID; + +public class HappyGhastEntity extends AnimalEntity { + public HappyGhastEntity(GeyserSession session, int entityId, long geyserId, UUID uuid, EntityDefinition definition, Vector3f position, Vector3f motion, float yaw, float pitch, float headYaw) { + super(session, entityId, geyserId, uuid, definition, position, motion, yaw, pitch, headYaw); + + setFlag(EntityFlag.CAN_FLY, true); + setFlag(EntityFlag.TAMED, true); + setFlag(EntityFlag.CAN_WALK, true); + + setFlag(EntityFlag.WASD_AIR_CONTROLLED, true); + setFlag(EntityFlag.DOES_SERVER_AUTH_ONLY_DISMOUNT, true); + + // TODO: verify which flags are necessary + + setAirSupply(100); + } + + @Override + @Nullable + protected Tag getFoodTag() { + return ItemTag.HAPPY_GHAST_FOOD; + } + + @Override + protected float getBabySize() { + return 0.2375f; + } + + public void setStaysStill(BooleanEntityMetadata entityMetadata) { + propertyManager.add("minecraft:can_move", !entityMetadata.getPrimitiveValue()); + updateBedrockEntityProperties(); + } + + @NonNull + @Override + protected InteractiveTag testMobInteraction(@NonNull Hand hand, @NonNull GeyserItemStack itemInHand) { + if (this.isBaby()) { + return super.testMobInteraction(hand, itemInHand); + } else { + if (!itemInHand.isEmpty()) { + if (session.getTagCache().is(ItemTag.HARNESSES, itemInHand)) { + if (!this.body.isValid()) { + // Harnesses the ghast + return InteractiveTag.HARNESS_HAPPY_GHAST; + } + } + // TODO: Handle shearing the harness off + } + + if (this.body.isValid() && !session.isSneaking()) { + // Rides happy ghast + return InteractiveTag.RIDE_HORSE; + } else { + return super.testMobInteraction(hand, itemInHand); + } + } + } + + @NonNull + @Override + protected InteractionResult mobInteract(@NonNull Hand hand, @NonNull GeyserItemStack itemInHand) { + if (this.isBaby()) { + return super.mobInteract(hand, itemInHand); + } else { + if (!itemInHand.isEmpty()) { + if (session.getTagCache().is(ItemTag.HARNESSES, itemInHand)) { + if (!this.body.isValid()) { + // Harnesses the ghast + return InteractionResult.SUCCESS; + } + } + // TODO: Handle shearing the harness off + } + + if (this.body.isValid() && !session.isSneaking()) { + // Rides happy ghast + return InteractionResult.SUCCESS; + } else { + return super.mobInteract(hand, itemInHand); + } + } + } +} diff --git a/core/src/main/java/org/geysermc/geyser/network/UpstreamPacketHandler.java b/core/src/main/java/org/geysermc/geyser/network/UpstreamPacketHandler.java index c2b47bfcb..55ca4ebe5 100644 --- a/core/src/main/java/org/geysermc/geyser/network/UpstreamPacketHandler.java +++ b/core/src/main/java/org/geysermc/geyser/network/UpstreamPacketHandler.java @@ -242,6 +242,8 @@ public class UpstreamPacketHandler extends LoggingPacketHandler { stackPacket.getResourcePacks().addAll(this.resourcePackLoadEvent.orderedPacks()); // Allows Vibrant Visuals to be toggled in the settings stackPacket.getExperiments().add(new ExperimentData("experimental_graphics", true)); + // Enables 2025 Content Drop 2 features + stackPacket.getExperiments().add(new ExperimentData("y_2025_drop_2", true)); session.sendUpstreamPacket(stackPacket); } diff --git a/core/src/main/java/org/geysermc/geyser/session/GeyserSession.java b/core/src/main/java/org/geysermc/geyser/session/GeyserSession.java index 722be5d9f..e60d9c2fb 100644 --- a/core/src/main/java/org/geysermc/geyser/session/GeyserSession.java +++ b/core/src/main/java/org/geysermc/geyser/session/GeyserSession.java @@ -1691,6 +1691,8 @@ public class GeyserSession implements GeyserConnection, GeyserCommandSource { startGamePacket.getExperiments().add(new ExperimentData("experimental_molang_features", true)); // Allows Vibrant Visuals to appear in the settings menu startGamePacket.getExperiments().add(new ExperimentData("experimental_graphics", true)); + // Enables 2025 Content Drop 2 features + startGamePacket.getExperiments().add(new ExperimentData("y_2025_drop_2", true)); startGamePacket.setVanillaVersion("*"); startGamePacket.setInventoriesServerAuthoritative(true); diff --git a/core/src/main/java/org/geysermc/geyser/util/InteractiveTag.java b/core/src/main/java/org/geysermc/geyser/util/InteractiveTag.java index 168c861f5..872bc94b4 100644 --- a/core/src/main/java/org/geysermc/geyser/util/InteractiveTag.java +++ b/core/src/main/java/org/geysermc/geyser/util/InteractiveTag.java @@ -73,7 +73,8 @@ public enum InteractiveTag { GIVE_ITEM_TO_ALLAY("allay"), EQUIP_WOLF_ARMOR("equipwolfarmor"), REMOVE_WOLF_ARMOR("removewolfarmor"), - REPAIR_WOLF_ARMOR("repairwolfarmor"); + REPAIR_WOLF_ARMOR("repairwolfarmor"), + HARNESS_HAPPY_GHAST("equipharness"); /** * The full string that should be passed on to the client. diff --git a/core/src/main/resources/bedrock/entity_identifiers.dat b/core/src/main/resources/bedrock/entity_identifiers.dat index 2dd5602491f79aaa74aa73fcbeace6cd434ab6ae..6c40d13a6a58edd84cf6e9036d6b243d911f21c9 100644 GIT binary patch delta 63 zcmdnx_}7t_i-D6ZGbJapxP+^PX|f}u#N;#F+#3zt6edm(1~COQ5(^3{y^0@Y=TmY8F3AF$K