mirror of
https://github.com/GeyserMC/Geyser.git
synced 2025-02-17 02:34:17 +01:00
Boats are leashable
This commit is contained in:
parent
65fd409a00
commit
8ad10f8a9e
17 changed files with 120 additions and 38 deletions
core/src/main/java/org/geysermc/geyser
|
@ -41,7 +41,7 @@ import org.geysermc.mcprotocollib.protocol.data.game.entity.player.Hand;
|
|||
|
||||
import java.util.UUID;
|
||||
|
||||
public class BoatEntity extends Entity implements Tickable {
|
||||
public class BoatEntity extends Entity implements Leashable, Tickable {
|
||||
|
||||
/**
|
||||
* Required when IS_BUOYANT is sent in order for boats to work in the water. <br>
|
||||
|
@ -65,6 +65,8 @@ public class BoatEntity extends Entity implements Tickable {
|
|||
@Getter
|
||||
private int variant;
|
||||
|
||||
private long leashHolderBedrockId = -1;
|
||||
|
||||
// Looks too fast and too choppy with 0.1f, which is how I believe the Microsoftian client handles it
|
||||
private final float ROWING_SPEED = 0.1f;
|
||||
|
||||
|
@ -147,8 +149,18 @@ public class BoatEntity extends Entity implements Tickable {
|
|||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setLeashHolderBedrockId(long bedrockId) {
|
||||
this.leashHolderBedrockId = bedrockId;
|
||||
dirtyMetadata.put(EntityDataTypes.LEASH_HOLDER, bedrockId);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected InteractiveTag testInteraction(Hand hand) {
|
||||
InteractiveTag tag = super.testInteraction(hand);
|
||||
if (tag != InteractiveTag.NONE) {
|
||||
return tag;
|
||||
}
|
||||
if (session.isSneaking()) {
|
||||
return InteractiveTag.NONE;
|
||||
} else if (passengers.size() < 2) {
|
||||
|
@ -160,6 +172,10 @@ public class BoatEntity extends Entity implements Tickable {
|
|||
|
||||
@Override
|
||||
public InteractionResult interact(Hand hand) {
|
||||
InteractionResult result = super.interact(hand);
|
||||
if (result != InteractionResult.PASS) {
|
||||
return result;
|
||||
}
|
||||
if (session.isSneaking()) {
|
||||
return InteractionResult.PASS;
|
||||
} else {
|
||||
|
@ -191,6 +207,11 @@ public class BoatEntity extends Entity implements Tickable {
|
|||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public long leashHolderBedrockId() {
|
||||
return leashHolderBedrockId;
|
||||
}
|
||||
|
||||
private void sendAnimationPacket(GeyserSession session, Entity rower, AnimatePacket.Action action, float rowTime) {
|
||||
AnimatePacket packet = new AnimatePacket();
|
||||
packet.setRuntimeEntityId(rower.getGeyserId());
|
||||
|
|
|
@ -40,6 +40,7 @@ import org.geysermc.geyser.api.entity.type.GeyserEntity;
|
|||
import org.geysermc.geyser.entity.EntityDefinition;
|
||||
import org.geysermc.geyser.entity.GeyserDirtyMetadata;
|
||||
import org.geysermc.geyser.entity.properties.GeyserEntityPropertyManager;
|
||||
import org.geysermc.geyser.item.Items;
|
||||
import org.geysermc.geyser.session.GeyserSession;
|
||||
import org.geysermc.geyser.translator.text.MessageTranslator;
|
||||
import org.geysermc.geyser.util.EntityUtils;
|
||||
|
@ -557,6 +558,17 @@ public class Entity implements GeyserEntity {
|
|||
* Should usually mirror {@link #interact(Hand)} without any side effects.
|
||||
*/
|
||||
protected InteractiveTag testInteraction(Hand hand) {
|
||||
if (isAlive() && this instanceof Leashable leashable) {
|
||||
if (leashable.leashHolderBedrockId() == session.getPlayerEntity().getGeyserId()) {
|
||||
// Note this might be client side. Has yet to be an issue though, as of Java 1.21.
|
||||
return InteractiveTag.REMOVE_LEASH;
|
||||
}
|
||||
if (session.getPlayerInventory().getItemInHand(hand).asItem() == Items.LEAD && leashable.canBeLeashed()) {
|
||||
// We shall leash
|
||||
return InteractiveTag.LEASH;
|
||||
}
|
||||
}
|
||||
|
||||
return InteractiveTag.NONE;
|
||||
}
|
||||
|
||||
|
@ -565,6 +577,18 @@ public class Entity implements GeyserEntity {
|
|||
* to ensure packet parity as well as functionality parity (such as sound effect responses).
|
||||
*/
|
||||
public InteractionResult interact(Hand hand) {
|
||||
if (isAlive() && this instanceof Leashable leashable) {
|
||||
if (leashable.leashHolderBedrockId() == session.getPlayerEntity().getGeyserId()) {
|
||||
// Note this might also update client side (a theoretical Geyser/client desync and Java parity issue).
|
||||
// Has yet to be an issue though, as of Java 1.21.
|
||||
return InteractionResult.SUCCESS;
|
||||
}
|
||||
if (session.getPlayerInventory().getItemInHand(hand).asItem() == Items.LEAD && leashable.canBeLeashed()) {
|
||||
// We shall leash
|
||||
return InteractionResult.SUCCESS;
|
||||
}
|
||||
}
|
||||
|
||||
return InteractionResult.PASS;
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,44 @@
|
|||
/*
|
||||
* Copyright (c) 2024 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;
|
||||
|
||||
/**
|
||||
* I can haz lead
|
||||
* (The item, not the mineral)
|
||||
*/
|
||||
public interface Leashable {
|
||||
void setLeashHolderBedrockId(long bedrockId);
|
||||
|
||||
long leashHolderBedrockId();
|
||||
|
||||
default boolean canBeLeashed() {
|
||||
return isNotLeashed();
|
||||
}
|
||||
|
||||
default boolean isNotLeashed() {
|
||||
return leashHolderBedrockId() == -1L;
|
||||
}
|
||||
}
|
|
@ -38,7 +38,7 @@ public class AmbientEntity extends MobEntity {
|
|||
}
|
||||
|
||||
@Override
|
||||
protected boolean canBeLeashed() {
|
||||
public boolean canBeLeashed() {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -43,7 +43,7 @@ public class DolphinEntity extends WaterEntity {
|
|||
}
|
||||
|
||||
@Override
|
||||
protected boolean canBeLeashed() {
|
||||
public boolean canBeLeashed() {
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
|
@ -25,12 +25,12 @@
|
|||
|
||||
package org.geysermc.geyser.entity.type.living;
|
||||
|
||||
import lombok.Getter;
|
||||
import org.checkerframework.checker.nullness.qual.NonNull;
|
||||
import org.cloudburstmc.math.vector.Vector3f;
|
||||
import org.cloudburstmc.protocol.bedrock.data.entity.EntityDataTypes;
|
||||
import org.cloudburstmc.protocol.bedrock.data.entity.EntityFlag;
|
||||
import org.geysermc.geyser.entity.EntityDefinition;
|
||||
import org.geysermc.geyser.entity.type.Leashable;
|
||||
import org.geysermc.geyser.entity.type.LivingEntity;
|
||||
import org.geysermc.geyser.inventory.GeyserItemStack;
|
||||
import org.geysermc.geyser.item.Items;
|
||||
|
@ -43,11 +43,10 @@ import org.geysermc.mcprotocollib.protocol.data.game.entity.player.Hand;
|
|||
|
||||
import java.util.UUID;
|
||||
|
||||
public class MobEntity extends LivingEntity {
|
||||
public class MobEntity extends LivingEntity implements Leashable {
|
||||
/**
|
||||
* If another mob is holding this mob by a leash, this variable tracks their Bedrock entity ID.
|
||||
*/
|
||||
@Getter
|
||||
private long leashHolderBedrockId;
|
||||
|
||||
public MobEntity(GeyserSession session, int entityId, long geyserId, UUID uuid, EntityDefinition<?> definition, Vector3f position, Vector3f motion, float yaw, float pitch, float headYaw) {
|
||||
|
@ -65,6 +64,7 @@ public class MobEntity extends LivingEntity {
|
|||
setFlag(EntityFlag.NO_AI, (xd & 0x01) == 0x01);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setLeashHolderBedrockId(long bedrockId) {
|
||||
this.leashHolderBedrockId = bedrockId;
|
||||
dirtyMetadata.put(EntityDataTypes.LEASH_HOLDER, bedrockId);
|
||||
|
@ -79,10 +79,7 @@ public class MobEntity extends LivingEntity {
|
|||
return InteractiveTag.REMOVE_LEASH;
|
||||
} else {
|
||||
GeyserItemStack itemStack = session.getPlayerInventory().getItemInHand(hand);
|
||||
if (itemStack.asItem() == Items.LEAD && canBeLeashed()) {
|
||||
// We shall leash
|
||||
return InteractiveTag.LEASH;
|
||||
} else if (itemStack.asItem() == Items.NAME_TAG) {
|
||||
if (itemStack.asItem() == Items.NAME_TAG) {
|
||||
InteractionResult result = checkInteractWithNameTag(itemStack);
|
||||
if (result.consumesAction()) {
|
||||
return InteractiveTag.NAME;
|
||||
|
@ -99,9 +96,6 @@ public class MobEntity extends LivingEntity {
|
|||
if (!isAlive()) {
|
||||
// dead lol
|
||||
return InteractionResult.PASS;
|
||||
} else if (leashHolderBedrockId == session.getPlayerEntity().getGeyserId()) {
|
||||
// TODO looks like the client assumes it will go through and removes the attachment itself?
|
||||
return InteractionResult.SUCCESS;
|
||||
} else {
|
||||
GeyserItemStack itemInHand = session.getPlayerInventory().getItemInHand(hand);
|
||||
InteractionResult result = checkPriorityInteractions(itemInHand);
|
||||
|
@ -115,10 +109,7 @@ public class MobEntity extends LivingEntity {
|
|||
}
|
||||
|
||||
private InteractionResult checkPriorityInteractions(GeyserItemStack itemInHand) {
|
||||
if (itemInHand.asItem() == Items.LEAD && canBeLeashed()) {
|
||||
// We shall leash
|
||||
return InteractionResult.SUCCESS;
|
||||
} else if (itemInHand.asItem() == Items.NAME_TAG) {
|
||||
if (itemInHand.asItem() == Items.NAME_TAG) {
|
||||
InteractionResult result = checkInteractWithNameTag(itemInHand);
|
||||
if (result.consumesAction()) {
|
||||
return result;
|
||||
|
@ -143,12 +134,14 @@ public class MobEntity extends LivingEntity {
|
|||
return InteractionResult.PASS;
|
||||
}
|
||||
|
||||
protected boolean canBeLeashed() {
|
||||
@Override
|
||||
public boolean canBeLeashed() {
|
||||
return isNotLeashed() && !isEnemy();
|
||||
}
|
||||
|
||||
protected final boolean isNotLeashed() {
|
||||
return leashHolderBedrockId == -1L;
|
||||
@Override
|
||||
public long leashHolderBedrockId() {
|
||||
return leashHolderBedrockId;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -122,7 +122,7 @@ public class SquidEntity extends WaterEntity implements Tickable {
|
|||
}
|
||||
|
||||
@Override
|
||||
protected boolean canBeLeashed() {
|
||||
public boolean canBeLeashed() {
|
||||
return isNotLeashed();
|
||||
}
|
||||
|
||||
|
|
|
@ -38,7 +38,7 @@ public class WaterEntity extends CreatureEntity {
|
|||
}
|
||||
|
||||
@Override
|
||||
protected boolean canBeLeashed() {
|
||||
public boolean canBeLeashed() {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -72,7 +72,7 @@ public class AxolotlEntity extends AnimalEntity {
|
|||
}
|
||||
|
||||
@Override
|
||||
protected boolean canBeLeashed() {
|
||||
public boolean canBeLeashed() {
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
|
@ -63,7 +63,7 @@ public class HoglinEntity extends AnimalEntity {
|
|||
}
|
||||
|
||||
@Override
|
||||
protected boolean canBeLeashed() {
|
||||
public boolean canBeLeashed() {
|
||||
return isNotLeashed();
|
||||
}
|
||||
|
||||
|
|
|
@ -123,7 +123,7 @@ public class PandaEntity extends AnimalEntity {
|
|||
}
|
||||
|
||||
@Override
|
||||
protected boolean canBeLeashed() {
|
||||
public boolean canBeLeashed() {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
|
|
@ -56,7 +56,7 @@ public class TurtleEntity extends AnimalEntity {
|
|||
}
|
||||
|
||||
@Override
|
||||
protected boolean canBeLeashed() {
|
||||
public boolean canBeLeashed() {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -84,7 +84,7 @@ public abstract class TameableEntity extends AnimalEntity {
|
|||
}
|
||||
|
||||
@Override
|
||||
protected boolean canBeLeashed() {
|
||||
public boolean canBeLeashed() {
|
||||
return isNotLeashed();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -127,7 +127,7 @@ public class WolfEntity extends TameableEntity {
|
|||
}
|
||||
|
||||
@Override
|
||||
protected boolean canBeLeashed() {
|
||||
public boolean canBeLeashed() {
|
||||
return !getFlag(EntityFlag.ANGRY) && super.canBeLeashed();
|
||||
}
|
||||
|
||||
|
|
|
@ -47,7 +47,7 @@ public class AbstractMerchantEntity extends AgeableEntity {
|
|||
}
|
||||
|
||||
@Override
|
||||
protected boolean canBeLeashed() {
|
||||
public boolean canBeLeashed() {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
|
|
@ -58,7 +58,7 @@ public class ZoglinEntity extends MonsterEntity {
|
|||
}
|
||||
|
||||
@Override
|
||||
protected boolean canBeLeashed() {
|
||||
public boolean canBeLeashed() {
|
||||
return isNotLeashed();
|
||||
}
|
||||
|
||||
|
|
|
@ -25,15 +25,15 @@
|
|||
|
||||
package org.geysermc.geyser.translator.protocol.java.entity;
|
||||
|
||||
import org.geysermc.mcprotocollib.protocol.packet.ingame.clientbound.entity.ClientboundSetEntityLinkPacket;
|
||||
import org.cloudburstmc.protocol.bedrock.data.entity.EntityEventType;
|
||||
import org.cloudburstmc.protocol.bedrock.data.entity.EntityFlag;
|
||||
import org.cloudburstmc.protocol.bedrock.packet.EntityEventPacket;
|
||||
import org.geysermc.geyser.entity.type.Entity;
|
||||
import org.geysermc.geyser.entity.type.living.MobEntity;
|
||||
import org.geysermc.geyser.entity.type.Leashable;
|
||||
import org.geysermc.geyser.session.GeyserSession;
|
||||
import org.geysermc.geyser.translator.protocol.PacketTranslator;
|
||||
import org.geysermc.geyser.translator.protocol.Translator;
|
||||
import org.geysermc.mcprotocollib.protocol.packet.ingame.clientbound.entity.ClientboundSetEntityLinkPacket;
|
||||
|
||||
/**
|
||||
* Called when a leash is attached, removed or updated from an entity
|
||||
|
@ -44,16 +44,16 @@ public class JavaSetEntityLinkTranslator extends PacketTranslator<ClientboundSet
|
|||
@Override
|
||||
public void translate(GeyserSession session, ClientboundSetEntityLinkPacket packet) {
|
||||
Entity holderId = session.getEntityCache().getEntityByJavaId(packet.getEntityId());
|
||||
if (!(holderId instanceof MobEntity mobEntity)) {
|
||||
if (!(holderId instanceof Leashable asLeashable)) {
|
||||
return;
|
||||
}
|
||||
|
||||
Entity attachedToId = session.getEntityCache().getEntityByJavaId(packet.getAttachedToId());
|
||||
if (attachedToId == null || packet.getAttachedToId() == 0) {
|
||||
// Is not being leashed
|
||||
mobEntity.setFlag(EntityFlag.LEASHED, false);
|
||||
mobEntity.setLeashHolderBedrockId(-1L);
|
||||
mobEntity.updateBedrockMetadata();
|
||||
holderId.setFlag(EntityFlag.LEASHED, false);
|
||||
asLeashable.setLeashHolderBedrockId(-1L);
|
||||
holderId.updateBedrockMetadata();
|
||||
EntityEventPacket eventPacket = new EntityEventPacket();
|
||||
eventPacket.setRuntimeEntityId(holderId.getGeyserId());
|
||||
eventPacket.setType(EntityEventType.REMOVE_LEASH);
|
||||
|
@ -62,8 +62,8 @@ public class JavaSetEntityLinkTranslator extends PacketTranslator<ClientboundSet
|
|||
return;
|
||||
}
|
||||
|
||||
mobEntity.setFlag(EntityFlag.LEASHED, true);
|
||||
mobEntity.setLeashHolderBedrockId(attachedToId.getGeyserId());
|
||||
holderId.setFlag(EntityFlag.LEASHED, true);
|
||||
asLeashable.setLeashHolderBedrockId(attachedToId.getGeyserId());
|
||||
holderId.updateBedrockMetadata();
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Add table
Reference in a new issue