Play effect when feeding baby animals (#2146)

Java plays a client-side-only effect when feeding animals. This commit abstracts out the feeding code we already have for interactive tag and checks it when right-clicking any animal that is a baby.
This commit is contained in:
Camotoy 2021-04-26 19:59:12 -04:00 committed by GitHub
parent 48fcb4733d
commit 7d80dff028
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
24 changed files with 245 additions and 117 deletions

View file

@ -34,4 +34,14 @@ public class AnimalEntity extends AgeableEntity {
public AnimalEntity(long entityId, long geyserId, EntityType entityType, Vector3f position, Vector3f motion, Vector3f rotation) { public AnimalEntity(long entityId, long geyserId, EntityType entityType, Vector3f position, Vector3f motion, Vector3f rotation) {
super(entityId, geyserId, entityType, position, motion, rotation); super(entityId, geyserId, entityType, position, motion, rotation);
} }
/**
* @param javaIdentifierStripped the stripped Java identifier of the item that is potential breeding food. For example,
* <code>wheat</code>.
* @return true if this is a valid item to breed with for this animal.
*/
public boolean canEat(String javaIdentifierStripped) {
// This is what it defaults to. OK.
return javaIdentifierStripped.equals("wheat");
}
} }

View file

@ -26,6 +26,7 @@
package org.geysermc.connector.entity.living.animal; package org.geysermc.connector.entity.living.animal;
import com.github.steveice10.mc.protocol.data.game.entity.metadata.EntityMetadata; import com.github.steveice10.mc.protocol.data.game.entity.metadata.EntityMetadata;
import com.google.common.collect.ImmutableSet;
import com.nukkitx.math.vector.Vector3f; import com.nukkitx.math.vector.Vector3f;
import com.nukkitx.protocol.bedrock.data.entity.EntityData; import com.nukkitx.protocol.bedrock.data.entity.EntityData;
import com.nukkitx.protocol.bedrock.data.entity.EntityEventType; import com.nukkitx.protocol.bedrock.data.entity.EntityEventType;
@ -34,7 +35,15 @@ import com.nukkitx.protocol.bedrock.packet.EntityEventPacket;
import org.geysermc.connector.entity.type.EntityType; import org.geysermc.connector.entity.type.EntityType;
import org.geysermc.connector.network.session.GeyserSession; import org.geysermc.connector.network.session.GeyserSession;
import java.util.Set;
public class BeeEntity extends AnimalEntity { public class BeeEntity extends AnimalEntity {
/**
* A list of all flowers. Used for feeding bees.
*/
private static final Set<String> FLOWERS = ImmutableSet.of("dandelion", "poppy", "blue_orchid", "allium", "azure_bluet",
"red_tulip", "pink_tulip", "white_tulip", "orange_tulip", "cornflower", "lily_of_the_valley", "wither_rose",
"sunflower", "lilac", "rose_bush", "peony");
public BeeEntity(long entityId, long geyserId, EntityType entityType, Vector3f position, Vector3f motion, Vector3f rotation) { public BeeEntity(long entityId, long geyserId, EntityType entityType, Vector3f position, Vector3f motion, Vector3f rotation) {
super(entityId, geyserId, entityType, position, motion, rotation); super(entityId, geyserId, entityType, position, motion, rotation);
@ -63,4 +72,9 @@ public class BeeEntity extends AnimalEntity {
} }
super.updateBedrockMetadata(entityMetadata, session); super.updateBedrockMetadata(entityMetadata, session);
} }
@Override
public boolean canEat(String javaIdentifierStripped) {
return FLOWERS.contains(javaIdentifierStripped);
}
} }

View file

@ -0,0 +1,41 @@
/*
* Copyright (c) 2019-2021 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.connector.entity.living.animal;
import com.nukkitx.math.vector.Vector3f;
import org.geysermc.connector.entity.type.EntityType;
public class ChickenEntity extends AnimalEntity {
public ChickenEntity(long entityId, long geyserId, EntityType entityType, Vector3f position, Vector3f motion, Vector3f rotation) {
super(entityId, geyserId, entityType, position, motion, rotation);
}
@Override
public boolean canEat(String javaIdentifierStripped) {
return javaIdentifierStripped.contains("seeds");
}
}

View file

@ -52,4 +52,9 @@ public class FoxEntity extends AnimalEntity {
} }
super.updateBedrockMetadata(entityMetadata, session); super.updateBedrockMetadata(entityMetadata, session);
} }
@Override
public boolean canEat(String javaIdentifierStripped) {
return javaIdentifierStripped.equals("sweet_berries");
}
} }

View file

@ -47,4 +47,9 @@ public class HoglinEntity extends AnimalEntity {
} }
super.updateBedrockMetadata(entityMetadata, session); super.updateBedrockMetadata(entityMetadata, session);
} }
@Override
public boolean canEat(String javaIdentifierStripped) {
return javaIdentifierStripped.equals("crimson_fungus");
}
} }

View file

@ -44,4 +44,9 @@ public class OcelotEntity extends AnimalEntity {
} }
super.updateBedrockMetadata(entityMetadata, session); super.updateBedrockMetadata(entityMetadata, session);
} }
@Override
public boolean canEat(String javaIdentifierStripped) {
return javaIdentifierStripped.equals("cod") || javaIdentifierStripped.equals("salmon");
}
} }

View file

@ -79,6 +79,11 @@ public class PandaEntity extends AnimalEntity {
super.updateBedrockMetadata(entityMetadata, session); super.updateBedrockMetadata(entityMetadata, session);
} }
@Override
public boolean canEat(String javaIdentifierStripped) {
return javaIdentifierStripped.equals("bamboo");
}
/** /**
* Update the panda's appearance, and take into consideration the recessive brown and weak traits that only show up * Update the panda's appearance, and take into consideration the recessive brown and weak traits that only show up
* when both main and hidden genes match * when both main and hidden genes match

View file

@ -46,6 +46,11 @@ public class PigEntity extends AnimalEntity {
super.updateBedrockMetadata(entityMetadata, session); super.updateBedrockMetadata(entityMetadata, session);
} }
@Override
public boolean canEat(String javaIdentifierStripped) {
return javaIdentifierStripped.equals("carrot") || javaIdentifierStripped.equals("potato") || javaIdentifierStripped.equals("beetroot");
}
@Override @Override
protected float getDefaultMaxHealth() { protected float getDefaultMaxHealth() {
return 10f; return 10f;

View file

@ -44,4 +44,9 @@ public class PolarBearEntity extends AnimalEntity {
} }
super.updateBedrockMetadata(entityMetadata, session); super.updateBedrockMetadata(entityMetadata, session);
} }
@Override
public boolean canEat(String javaIdentifierStripped) {
return false;
}
} }

View file

@ -59,4 +59,9 @@ public class RabbitEntity extends AnimalEntity {
metadata.put(EntityData.VARIANT, variant); metadata.put(EntityData.VARIANT, variant);
} }
} }
@Override
public boolean canEat(String javaIdentifierStripped) {
return javaIdentifierStripped.equals("dandelion") || javaIdentifierStripped.equals("carrot") || javaIdentifierStripped.equals("golden_carrot");
}
} }

View file

@ -85,4 +85,9 @@ public class StriderEntity extends AnimalEntity {
super.updateBedrockMetadata(session); super.updateBedrockMetadata(session);
} }
@Override
public boolean canEat(String javaIdentifierStripped) {
return javaIdentifierStripped.equals("warped_fungus");
}
} }

View file

@ -46,4 +46,9 @@ public class TurtleEntity extends AnimalEntity {
} }
super.updateBedrockMetadata(entityMetadata, session); super.updateBedrockMetadata(entityMetadata, session);
} }
@Override
public boolean canEat(String javaIdentifierStripped) {
return javaIdentifierStripped.equals("seagrass");
}
} }

View file

@ -26,6 +26,7 @@
package org.geysermc.connector.entity.living.animal.horse; package org.geysermc.connector.entity.living.animal.horse;
import com.github.steveice10.mc.protocol.data.game.entity.metadata.EntityMetadata; import com.github.steveice10.mc.protocol.data.game.entity.metadata.EntityMetadata;
import com.google.common.collect.ImmutableSet;
import com.nukkitx.math.vector.Vector3f; import com.nukkitx.math.vector.Vector3f;
import com.nukkitx.protocol.bedrock.data.entity.EntityData; import com.nukkitx.protocol.bedrock.data.entity.EntityData;
import com.nukkitx.protocol.bedrock.data.entity.EntityEventType; import com.nukkitx.protocol.bedrock.data.entity.EntityEventType;
@ -38,7 +39,15 @@ import org.geysermc.connector.entity.type.EntityType;
import org.geysermc.connector.network.session.GeyserSession; import org.geysermc.connector.network.session.GeyserSession;
import org.geysermc.connector.network.translators.item.ItemRegistry; import org.geysermc.connector.network.translators.item.ItemRegistry;
import java.util.Set;
public class AbstractHorseEntity extends AnimalEntity { public class AbstractHorseEntity extends AnimalEntity {
/**
* A list of all foods a horse/donkey can eat on Java Edition.
* Used to display interactive tag if needed.
*/
private static final Set<String> DONKEY_AND_HORSE_FOODS = ImmutableSet.of("golden_apple", "enchanted_golden_apple",
"golden_carrot", "sugar", "apple", "wheat", "hay_block");
public AbstractHorseEntity(long entityId, long geyserId, EntityType entityType, Vector3f position, Vector3f motion, Vector3f rotation) { public AbstractHorseEntity(long entityId, long geyserId, EntityType entityType, Vector3f position, Vector3f motion, Vector3f rotation) {
super(entityId, geyserId, entityType, position, motion, rotation); super(entityId, geyserId, entityType, position, motion, rotation);
@ -101,4 +110,9 @@ public class AbstractHorseEntity extends AnimalEntity {
updateBedrockAttributes(session); updateBedrockAttributes(session);
} }
} }
@Override
public boolean canEat(String javaIdentifierStripped) {
return DONKEY_AND_HORSE_FOODS.contains(javaIdentifierStripped);
}
} }

View file

@ -75,4 +75,9 @@ public class LlamaEntity extends ChestedHorseEntity {
} }
super.updateBedrockMetadata(entityMetadata, session); super.updateBedrockMetadata(entityMetadata, session);
} }
@Override
public boolean canEat(String javaIdentifierStripped) {
return javaIdentifierStripped.equals("wheat") || javaIdentifierStripped.equals("hay_block");
}
} }

View file

@ -83,4 +83,9 @@ public class CatEntity extends TameableEntity {
} }
} }
} }
@Override
public boolean canEat(String javaIdentifierStripped) {
return javaIdentifierStripped.equals("cod") || javaIdentifierStripped.equals("salmon");
}
} }

View file

@ -45,4 +45,9 @@ public class ParrotEntity extends TameableEntity {
} }
super.updateBedrockMetadata(entityMetadata, session); super.updateBedrockMetadata(entityMetadata, session);
} }
@Override
public boolean canEat(String javaIdentifierStripped) {
return javaIdentifierStripped.contains("seeds") || javaIdentifierStripped.equals("cookie");
}
} }

View file

@ -26,13 +26,23 @@
package org.geysermc.connector.entity.living.animal.tameable; package org.geysermc.connector.entity.living.animal.tameable;
import com.github.steveice10.mc.protocol.data.game.entity.metadata.EntityMetadata; import com.github.steveice10.mc.protocol.data.game.entity.metadata.EntityMetadata;
import com.google.common.collect.ImmutableSet;
import com.nukkitx.math.vector.Vector3f; import com.nukkitx.math.vector.Vector3f;
import com.nukkitx.protocol.bedrock.data.entity.EntityData; import com.nukkitx.protocol.bedrock.data.entity.EntityData;
import com.nukkitx.protocol.bedrock.data.entity.EntityFlag; import com.nukkitx.protocol.bedrock.data.entity.EntityFlag;
import org.geysermc.connector.entity.type.EntityType; import org.geysermc.connector.entity.type.EntityType;
import org.geysermc.connector.network.session.GeyserSession; import org.geysermc.connector.network.session.GeyserSession;
import java.util.Set;
public class WolfEntity extends TameableEntity { public class WolfEntity extends TameableEntity {
/**
* A list of all foods a wolf can eat on Java Edition.
* Used to display interactive tag or particles if needed.
*/
private static final Set<String> WOLF_FOODS = ImmutableSet.of("pufferfish", "tropical_fish", "chicken", "cooked_chicken",
"porkchop", "beef", "rabbit", "cooked_porkchop", "cooked_beef", "rotten_flesh", "mutton", "cooked_mutton",
"cooked_rabbit");
private byte collarColor; private byte collarColor;
@ -75,4 +85,10 @@ public class WolfEntity extends TameableEntity {
super.updateBedrockMetadata(entityMetadata, session); super.updateBedrockMetadata(entityMetadata, session);
} }
@Override
public boolean canEat(String javaIdentifierStripped) {
// Cannot be a baby to eat these foods
return WOLF_FOODS.contains(javaIdentifierStripped) && !metadata.getFlags().getFlag(EntityFlag.BABY);
}
} }

View file

@ -45,7 +45,7 @@ import java.util.List;
@Getter @Getter
public enum EntityType { public enum EntityType {
CHICKEN(AnimalEntity.class, 10, 0.7f, 0.4f), CHICKEN(ChickenEntity.class, 10, 0.7f, 0.4f),
COW(AnimalEntity.class, 11, 1.4f, 0.9f), COW(AnimalEntity.class, 11, 1.4f, 0.9f),
PIG(PigEntity.class, 12, 0.9f), PIG(PigEntity.class, 12, 0.9f),
SHEEP(SheepEntity.class, 13, 1.3f, 0.9f), SHEEP(SheepEntity.class, 13, 1.3f, 0.9f),

View file

@ -25,11 +25,9 @@
package org.geysermc.connector.network.translators.sound; package org.geysermc.connector.network.translators.sound;
import com.github.steveice10.mc.protocol.data.game.entity.metadata.ItemStack;
import com.nukkitx.math.vector.Vector3f; import com.nukkitx.math.vector.Vector3f;
import org.geysermc.connector.inventory.GeyserItemStack; import org.geysermc.connector.inventory.GeyserItemStack;
import org.geysermc.connector.network.session.GeyserSession; import org.geysermc.connector.network.session.GeyserSession;
import org.geysermc.connector.network.translators.item.ItemRegistry;
import java.util.Map; import java.util.Map;
@ -47,6 +45,9 @@ public interface BlockSoundInteractionHandler extends SoundInteractionHandler<St
* @param identifier the identifier of the block * @param identifier the identifier of the block
*/ */
static void handleBlockInteraction(GeyserSession session, Vector3f position, String identifier) { static void handleBlockInteraction(GeyserSession session, Vector3f position, String identifier) {
// If we need to get the hand identifier, only get it once and save it to a variable
String handIdentifier = null;
for (Map.Entry<SoundHandler, SoundInteractionHandler<?>> interactionEntry : SoundHandlerRegistry.INTERACTION_HANDLERS.entrySet()) { for (Map.Entry<SoundHandler, SoundInteractionHandler<?>> interactionEntry : SoundHandlerRegistry.INTERACTION_HANDLERS.entrySet()) {
if (!(interactionEntry.getValue() instanceof BlockSoundInteractionHandler)) { if (!(interactionEntry.getValue() instanceof BlockSoundInteractionHandler)) {
continue; continue;
@ -66,7 +67,9 @@ public interface BlockSoundInteractionHandler extends SoundInteractionHandler<St
if (itemInHand.isEmpty()) { if (itemInHand.isEmpty()) {
continue; continue;
} }
String handIdentifier = itemInHand.getItemEntry().getJavaIdentifier(); if (handIdentifier == null) {
handIdentifier = itemInHand.getItemEntry().getJavaIdentifier();
}
boolean contains = false; boolean contains = false;
for (String itemIdentifier : interactionEntry.getKey().items()) { for (String itemIdentifier : interactionEntry.getKey().items()) {
if (handIdentifier.contains(itemIdentifier)) { if (handIdentifier.contains(itemIdentifier)) {

View file

@ -25,12 +25,10 @@
package org.geysermc.connector.network.translators.sound; package org.geysermc.connector.network.translators.sound;
import com.github.steveice10.mc.protocol.data.game.entity.metadata.ItemStack;
import com.nukkitx.math.vector.Vector3f; import com.nukkitx.math.vector.Vector3f;
import org.geysermc.connector.entity.Entity; import org.geysermc.connector.entity.Entity;
import org.geysermc.connector.inventory.GeyserItemStack; import org.geysermc.connector.inventory.GeyserItemStack;
import org.geysermc.connector.network.session.GeyserSession; import org.geysermc.connector.network.session.GeyserSession;
import org.geysermc.connector.network.translators.item.ItemRegistry;
import java.util.Map; import java.util.Map;
@ -48,6 +46,9 @@ public interface EntitySoundInteractionHandler extends SoundInteractionHandler<E
* @param entity the entity interacted with * @param entity the entity interacted with
*/ */
static void handleEntityInteraction(GeyserSession session, Vector3f position, Entity entity) { static void handleEntityInteraction(GeyserSession session, Vector3f position, Entity entity) {
// If we need to get the hand identifier, only get it once and save it to a variable
String handIdentifier = null;
for (Map.Entry<SoundHandler, SoundInteractionHandler<?>> interactionEntry : SoundHandlerRegistry.INTERACTION_HANDLERS.entrySet()) { for (Map.Entry<SoundHandler, SoundInteractionHandler<?>> interactionEntry : SoundHandlerRegistry.INTERACTION_HANDLERS.entrySet()) {
if (!(interactionEntry.getValue() instanceof EntitySoundInteractionHandler)) { if (!(interactionEntry.getValue() instanceof EntitySoundInteractionHandler)) {
continue; continue;
@ -67,7 +68,10 @@ public interface EntitySoundInteractionHandler extends SoundInteractionHandler<E
if (itemInHand.isEmpty()) { if (itemInHand.isEmpty()) {
continue; continue;
} }
String handIdentifier = itemInHand.getItemEntry().getJavaIdentifier(); if (handIdentifier == null) {
// Don't get the identifier unless we need it
handIdentifier = itemInHand.getItemEntry().getJavaIdentifier();
}
boolean contains = false; boolean contains = false;
for (String itemIdentifier : interactionEntry.getKey().items()) { for (String itemIdentifier : interactionEntry.getKey().items()) {
if (handIdentifier.contains(itemIdentifier)) { if (handIdentifier.contains(itemIdentifier)) {

View file

@ -59,7 +59,7 @@ public @interface SoundHandler {
* Leave empty to ignore. * Leave empty to ignore.
* *
* Only applies to interaction handlers that are an * Only applies to interaction handlers that are an
* instance of {@link BlockSoundInteractionHandler}. * instance of {@link EntitySoundInteractionHandler}.
* *
* @return the value the item in the player's hand must contain * @return the value the item in the player's hand must contain
*/ */

View file

@ -33,7 +33,7 @@ import org.geysermc.connector.network.translators.sound.BlockSoundInteractionHan
import org.geysermc.connector.network.translators.sound.SoundHandler; import org.geysermc.connector.network.translators.sound.SoundHandler;
@SoundHandler(blocks = "comparator") @SoundHandler(blocks = "comparator")
public class ComparatorSoundInteractHandler implements BlockSoundInteractionHandler { public class ComparatorSoundInteractionHandler implements BlockSoundInteractionHandler {
@Override @Override
public void handleInteraction(GeyserSession session, Vector3f position, String identifier) { public void handleInteraction(GeyserSession session, Vector3f position, String identifier) {

View file

@ -0,0 +1,57 @@
/*
* Copyright (c) 2019-2021 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.connector.network.translators.sound.entity;
import com.nukkitx.math.vector.Vector3f;
import com.nukkitx.protocol.bedrock.data.entity.EntityEventType;
import com.nukkitx.protocol.bedrock.data.entity.EntityFlag;
import com.nukkitx.protocol.bedrock.packet.EntityEventPacket;
import org.geysermc.connector.entity.Entity;
import org.geysermc.connector.entity.living.animal.AnimalEntity;
import org.geysermc.connector.entity.living.animal.OcelotEntity;
import org.geysermc.connector.entity.living.animal.tameable.CatEntity;
import org.geysermc.connector.network.session.GeyserSession;
import org.geysermc.connector.network.translators.sound.EntitySoundInteractionHandler;
import org.geysermc.connector.network.translators.sound.SoundHandler;
@SoundHandler
public class FeedBabySoundInteractionHandler implements EntitySoundInteractionHandler {
@Override
public void handleInteraction(GeyserSession session, Vector3f position, Entity entity) {
if (entity instanceof AnimalEntity && !(entity instanceof CatEntity || entity instanceof OcelotEntity)) {
String handIdentifier = session.getPlayerInventory().getItemInHand().getItemEntry().getJavaIdentifier();
boolean isBaby = entity.getMetadata().getFlags().getFlag(EntityFlag.BABY);
if (isBaby && ((AnimalEntity) entity).canEat(handIdentifier.replace("minecraft:", ""))) {
// Play the "feed child" effect
EntityEventPacket feedEvent = new EntityEventPacket();
feedEvent.setRuntimeEntityId(entity.getGeyserId());
feedEvent.setType(EntityEventType.BABY_ANIMAL_FEED);
session.sendUpstreamPacket(feedEvent);
}
}
}
}

View file

@ -25,12 +25,12 @@
package org.geysermc.connector.utils; package org.geysermc.connector.utils;
import com.google.common.collect.ImmutableSet;
import com.nukkitx.protocol.bedrock.data.entity.EntityData; import com.nukkitx.protocol.bedrock.data.entity.EntityData;
import com.nukkitx.protocol.bedrock.data.entity.EntityDataMap; import com.nukkitx.protocol.bedrock.data.entity.EntityDataMap;
import com.nukkitx.protocol.bedrock.data.entity.EntityFlag; import com.nukkitx.protocol.bedrock.data.entity.EntityFlag;
import lombok.Getter; import lombok.Getter;
import org.geysermc.connector.entity.Entity; import org.geysermc.connector.entity.Entity;
import org.geysermc.connector.entity.living.animal.AnimalEntity;
import org.geysermc.connector.entity.living.animal.horse.HorseEntity; import org.geysermc.connector.entity.living.animal.horse.HorseEntity;
import org.geysermc.connector.entity.type.EntityType; import org.geysermc.connector.entity.type.EntityType;
import org.geysermc.connector.network.session.GeyserSession; import org.geysermc.connector.network.session.GeyserSession;
@ -40,20 +40,6 @@ import java.util.EnumSet;
import java.util.Set; import java.util.Set;
public class InteractiveTagManager { public class InteractiveTagManager {
/**
* A list of all foods a horse/donkey can eat on Java Edition.
* Used to display interactive tag if needed.
*/
private static final Set<String> DONKEY_AND_HORSE_FOODS = ImmutableSet.of("golden_apple", "enchanted_golden_apple",
"golden_carrot", "sugar", "apple", "wheat", "hay_block");
/**
* A list of all flowers. Used for feeding bees.
*/
private static final Set<String> FLOWERS = ImmutableSet.of("dandelion", "poppy", "blue_orchid", "allium", "azure_bluet",
"red_tulip", "pink_tulip", "white_tulip", "orange_tulip", "cornflower", "lily_of_the_valley", "wither_rose",
"sunflower", "lilac", "rose_bush", "peony");
/** /**
* All entity types that can be leashed on Java Edition * All entity types that can be leashed on Java Edition
*/ */
@ -66,14 +52,6 @@ public class InteractiveTagManager {
private static final Set<EntityType> SADDLEABLE_WHEN_TAMED_MOB_TYPES = EnumSet.of(EntityType.DONKEY, EntityType.HORSE, private static final Set<EntityType> SADDLEABLE_WHEN_TAMED_MOB_TYPES = EnumSet.of(EntityType.DONKEY, EntityType.HORSE,
EntityType.ZOMBIE_HORSE, EntityType.MULE); EntityType.ZOMBIE_HORSE, EntityType.MULE);
/**
* A list of all foods a wolf can eat on Java Edition.
* Used to display interactive tag if needed.
*/
private static final Set<String> WOLF_FOODS = ImmutableSet.of("pufferfish", "tropical_fish", "chicken", "cooked_chicken",
"porkchop", "beef", "rabbit", "cooked_porkchop", "cooked_beef", "rotten_flesh", "mutton", "cooked_mutton",
"cooked_rabbit");
/** /**
* Update the suggestion that the client currently has on their screen for this entity (for example, "Feed" or "Ride") * Update the suggestion that the client currently has on their screen for this entity (for example, "Feed" or "Ride")
* *
@ -85,9 +63,8 @@ public class InteractiveTagManager {
ItemEntry itemEntry = session.getPlayerInventory().getItemInHand().getItemEntry(); ItemEntry itemEntry = session.getPlayerInventory().getItemInHand().getItemEntry();
String javaIdentifierStripped = itemEntry.getJavaIdentifier().replace("minecraft:", ""); String javaIdentifierStripped = itemEntry.getJavaIdentifier().replace("minecraft:", "");
// TODO - in the future, update these in the metadata? So the client doesn't have to wiggle their cursor around for it to happen
// TODO - also, might be good to abstract out the eating thing. I know there will need to be food tracked for https://github.com/GeyserMC/Geyser/issues/1005 but not all food is breeding food
InteractiveTag interactiveTag = InteractiveTag.NONE; InteractiveTag interactiveTag = InteractiveTag.NONE;
if (entityMetadata.getLong(EntityData.LEASH_HOLDER_EID) == session.getPlayerEntity().getGeyserId()) { if (entityMetadata.getLong(EntityData.LEASH_HOLDER_EID) == session.getPlayerEntity().getGeyserId()) {
// Unleash the entity // Unleash the entity
interactiveTag = InteractiveTag.REMOVE_LEASH; interactiveTag = InteractiveTag.REMOVE_LEASH;
@ -105,31 +82,24 @@ public class InteractiveTagManager {
// Holding a leash and the mob is leashable for sure // Holding a leash and the mob is leashable for sure
// (Plugins can change this behavior so that's something to look into in the far far future) // (Plugins can change this behavior so that's something to look into in the far far future)
interactiveTag = InteractiveTag.LEASH; interactiveTag = InteractiveTag.LEASH;
} else if (interactEntity instanceof AnimalEntity && ((AnimalEntity) interactEntity).canEat(javaIdentifierStripped)) {
// This animal can be fed
interactiveTag = InteractiveTag.FEED;
} else { } else {
switch (interactEntity.getEntityType()) { switch (interactEntity.getEntityType()) {
case BEE: case BOAT:
if (FLOWERS.contains(javaIdentifierStripped)) { if (interactEntity.getPassengers().size() < 2) {
interactiveTag = InteractiveTag.FEED; interactiveTag = InteractiveTag.BOARD_BOAT;
} }
break; break;
case BOAT:
interactiveTag = InteractiveTag.BOARD_BOAT;
break;
case CAT: case CAT:
if (javaIdentifierStripped.equals("cod") || javaIdentifierStripped.equals("salmon")) { if (entityMetadata.getFlags().getFlag(EntityFlag.TAMED) &&
interactiveTag = InteractiveTag.FEED;
} else if (entityMetadata.getFlags().getFlag(EntityFlag.TAMED) &&
entityMetadata.getLong(EntityData.OWNER_EID) == session.getPlayerEntity().getGeyserId()) { entityMetadata.getLong(EntityData.OWNER_EID) == session.getPlayerEntity().getGeyserId()) {
// Tamed and owned by player - can sit/stand // Tamed and owned by player - can sit/stand
interactiveTag = entityMetadata.getFlags().getFlag(EntityFlag.SITTING) ? InteractiveTag.STAND : InteractiveTag.SIT; interactiveTag = entityMetadata.getFlags().getFlag(EntityFlag.SITTING) ? InteractiveTag.STAND : InteractiveTag.SIT;
break; break;
} }
break; break;
case CHICKEN:
if (javaIdentifierStripped.contains("seeds")) {
interactiveTag = InteractiveTag.FEED;
}
break;
case MOOSHROOM: case MOOSHROOM:
// Shear the mooshroom // Shear the mooshroom
if (javaIdentifierStripped.equals("shears")) { if (javaIdentifierStripped.equals("shears")) {
@ -143,9 +113,7 @@ public class InteractiveTagManager {
} }
// Fall down to COW as this works on mooshrooms // Fall down to COW as this works on mooshrooms
case COW: case COW:
if (javaIdentifierStripped.equals("wheat")) { if (javaIdentifierStripped.equals("bucket")) {
interactiveTag = InteractiveTag.FEED;
} else if (javaIdentifierStripped.equals("bucket")) {
// Milk the cow // Milk the cow
interactiveTag = InteractiveTag.MILK; interactiveTag = InteractiveTag.MILK;
} }
@ -175,69 +143,28 @@ public class InteractiveTagManager {
interactiveTag = InteractiveTag.OPEN_CONTAINER; interactiveTag = InteractiveTag.OPEN_CONTAINER;
break; break;
} }
// have another switch statement as, while these share mount attributes they don't share food
switch (interactEntity.getEntityType()) {
case LLAMA:
case TRADER_LLAMA:
if (javaIdentifierStripped.equals("wheat") || javaIdentifierStripped.equals("hay_block")) {
interactiveTag = InteractiveTag.FEED;
break;
}
case DONKEY:
case HORSE:
// Undead can't eat
if (DONKEY_AND_HORSE_FOODS.contains(javaIdentifierStripped)) {
interactiveTag = InteractiveTag.FEED;
break;
}
}
if (!entityMetadata.getFlags().getFlag(EntityFlag.BABY)) { if (!entityMetadata.getFlags().getFlag(EntityFlag.BABY)) {
// Can't ride a baby // Can't ride a baby
if (tamed) { if (tamed) {
interactiveTag = InteractiveTag.RIDE_HORSE; interactiveTag = InteractiveTag.RIDE_HORSE;
} else if (itemEntry.equals(ItemEntry.AIR)) { } else if (itemEntry.getJavaId() == 0) {
// Can't hide an untamed entity without having your hand empty // Can't hide an untamed entity without having your hand empty
interactiveTag = InteractiveTag.MOUNT; interactiveTag = InteractiveTag.MOUNT;
} }
} }
break; break;
case FOX:
if (javaIdentifierStripped.equals("sweet_berries")) {
interactiveTag = InteractiveTag.FEED;
}
break;
case HOGLIN:
if (javaIdentifierStripped.equals("crimson_fungus")) {
interactiveTag = InteractiveTag.FEED;
}
break;
case MINECART: case MINECART:
if (interactEntity.getPassengers().isEmpty()) {
interactiveTag = InteractiveTag.RIDE_MINECART; interactiveTag = InteractiveTag.RIDE_MINECART;
}
break; break;
case MINECART_CHEST: case MINECART_CHEST:
case MINECART_COMMAND_BLOCK: case MINECART_COMMAND_BLOCK:
case MINECART_HOPPER: case MINECART_HOPPER:
interactiveTag = InteractiveTag.OPEN_CONTAINER; interactiveTag = InteractiveTag.OPEN_CONTAINER;
break; break;
case OCELOT:
if (javaIdentifierStripped.equals("cod") || javaIdentifierStripped.equals("salmon")) {
interactiveTag = InteractiveTag.FEED;
}
break;
case PANDA:
if (javaIdentifierStripped.equals("bamboo")) {
interactiveTag = InteractiveTag.FEED;
}
break;
case PARROT:
if (javaIdentifierStripped.contains("seeds") || javaIdentifierStripped.equals("cookie")) {
interactiveTag = InteractiveTag.FEED;
}
break;
case PIG: case PIG:
if (javaIdentifierStripped.equals("carrot") || javaIdentifierStripped.equals("potato") || javaIdentifierStripped.equals("beetroot")) { if (entityMetadata.getFlags().getFlag(EntityFlag.SADDLED)) {
interactiveTag = InteractiveTag.FEED;
} else if (entityMetadata.getFlags().getFlag(EntityFlag.SADDLED)) {
interactiveTag = InteractiveTag.MOUNT; interactiveTag = InteractiveTag.MOUNT;
} }
break; break;
@ -246,15 +173,8 @@ public class InteractiveTagManager {
interactiveTag = InteractiveTag.BARTER; interactiveTag = InteractiveTag.BARTER;
} }
break; break;
case RABBIT:
if (javaIdentifierStripped.equals("dandelion") || javaIdentifierStripped.equals("carrot") || javaIdentifierStripped.equals("golden_carrot")) {
interactiveTag = InteractiveTag.FEED;
}
break;
case SHEEP: case SHEEP:
if (javaIdentifierStripped.equals("wheat")) { if (!entityMetadata.getFlags().getFlag(EntityFlag.SHEARED)) {
interactiveTag = InteractiveTag.FEED;
} else if (!entityMetadata.getFlags().getFlag(EntityFlag.SHEARED)) {
if (javaIdentifierStripped.equals("shears")) { if (javaIdentifierStripped.equals("shears")) {
// Shear the sheep // Shear the sheep
interactiveTag = InteractiveTag.SHEAR; interactiveTag = InteractiveTag.SHEAR;
@ -265,17 +185,10 @@ public class InteractiveTagManager {
} }
break; break;
case STRIDER: case STRIDER:
if (javaIdentifierStripped.equals("warped_fungus")) { if (entityMetadata.getFlags().getFlag(EntityFlag.SADDLED)) {
interactiveTag = InteractiveTag.FEED;
} else if (entityMetadata.getFlags().getFlag(EntityFlag.SADDLED)) {
interactiveTag = InteractiveTag.RIDE_STRIDER; interactiveTag = InteractiveTag.RIDE_STRIDER;
} }
break; break;
case TURTLE:
if (javaIdentifierStripped.equals("seagrass")) {
interactiveTag = InteractiveTag.FEED;
}
break;
case VILLAGER: case VILLAGER:
if (entityMetadata.getInt(EntityData.VARIANT) != 14 && entityMetadata.getInt(EntityData.VARIANT) != 0 if (entityMetadata.getInt(EntityData.VARIANT) != 14 && entityMetadata.getInt(EntityData.VARIANT) != 0
&& entityMetadata.getFloat(EntityData.SCALE) >= 0.75f) { // Not a nitwit, has a profession and is not a baby && entityMetadata.getFloat(EntityData.SCALE) >= 0.75f) { // Not a nitwit, has a profession and is not a baby
@ -289,10 +202,6 @@ public class InteractiveTagManager {
if (javaIdentifierStripped.equals("bone") && !entityMetadata.getFlags().getFlag(EntityFlag.TAMED)) { if (javaIdentifierStripped.equals("bone") && !entityMetadata.getFlags().getFlag(EntityFlag.TAMED)) {
// Bone and untamed - can tame // Bone and untamed - can tame
interactiveTag = InteractiveTag.TAME; interactiveTag = InteractiveTag.TAME;
} else if (WOLF_FOODS.contains(javaIdentifierStripped)) {
// Compatible food in hand - feed
// Sometimes just sits/stands when the wolf isn't hungry - there doesn't appear to be a way to fix this
interactiveTag = InteractiveTag.FEED;
} else if (entityMetadata.getFlags().getFlag(EntityFlag.TAMED) && } else if (entityMetadata.getFlags().getFlag(EntityFlag.TAMED) &&
entityMetadata.getLong(EntityData.OWNER_EID) == session.getPlayerEntity().getGeyserId()) { entityMetadata.getLong(EntityData.OWNER_EID) == session.getPlayerEntity().getGeyserId()) {
// Tamed and owned by player - can sit/stand // Tamed and owned by player - can sit/stand