package com.r3944realms.leashedplayer.content.entities.props; import com.mojang.datafixers.util.Either; import net.minecraft.core.BlockPos; import net.minecraft.network.protocol.game.ClientboundSetEntityLinkPacket; import net.minecraft.server.level.ServerLevel; import net.minecraft.world.entity.Entity; import net.minecraft.world.entity.decoration.LeashFenceKnotEntity; import net.minecraft.world.item.Items; import net.minecraft.world.level.Level; import net.minecraft.world.phys.AABB; import javax.annotation.Nullable; import java.util.List; import java.util.Optional; import java.util.UUID; import java.util.function.Predicate; public interface ChainLeashable { String LEASH_TAG = "chain_leash"; @Nullable ChainLeashable.ChainData getLeashData(); void setLeashData(@Nullable ChainLeashable.ChainData pLeashData); default boolean isLeashed() { return this.getLeashData() != null && this.getLeashData().leashHolder != null; } default boolean mayBeLeashed() { return this.getLeashData() != null; } default boolean canHaveALeashAttachedToIt() { return this.canBeLeashed() && !this.isLeashed(); } default boolean canBeLeashed() { return true; } default boolean handleLeashAtDistance(Entity pLeashHolder, float pDistance) { return true; } default void leashTooFarBehaviour() { this.dropLeash(true, true); } default void closeRangeLeashBehaviour(Entity pEntity) { } default void elasticRangeLeashBehaviour(Entity pLeashHolder, float pDistance) { legacyElasticRangeLeashBehaviour((Entity & ChainLeashable)this, pLeashHolder, pDistance); } @Nullable default Entity getLeashHolder() { return getLeashHolder((Entity & ChainLeashable)this); } private static void legacyElasticRangeLeashBehaviour(E pEntity, Entity pLeashHolder, float pDistance) { double d0 = (pLeashHolder.getX() - pEntity.getX()) / (double)pDistance; double d1 = (pLeashHolder.getY() - pEntity.getY()) / (double)pDistance; double d2 = (pLeashHolder.getZ() - pEntity.getZ()) / (double)pDistance; pEntity.setDeltaMovement( pEntity.getDeltaMovement().add(Math.copySign(d0 * d0 * 0.4, d0), Math.copySign(d1 * d1 * 0.4, d1), Math.copySign(d2 * d2 * 0.4, d2)) ); } @Nullable private static Entity getLeashHolder(E pEntity) { ChainLeashable.ChainData chainLeashable$chaindata = pEntity.getLeashData(); if (chainLeashable$chaindata == null) { return null; } else { if (chainLeashable$chaindata.delayedLeashHolderId != 0 && pEntity.level().isClientSide) { Entity entity = pEntity.level().getEntity(chainLeashable$chaindata.delayedLeashHolderId); if (entity instanceof Entity) { chainLeashable$chaindata.setLeashHolder(entity); } } return chainLeashable$chaindata.leashHolder; } } default void setLeashedTo(Entity pLeashHolder, boolean pBroadcastPacket) { setLeashedTo((Entity & ChainLeashable)this, pLeashHolder, pBroadcastPacket); } static List leashableInArea(Level pLevel, BlockPos pPos, Predicate pPredicate) { double d0 = 7.0; int i = pPos.getX(); int j = pPos.getY(); int k = pPos.getZ(); AABB aabb = new AABB((double)i - 7.0, (double)j - 7.0, (double)k - 7.0, (double)i + 7.0, (double)j + 7.0, (double)k + 7.0); return pLevel.getEntitiesOfClass(Entity.class, aabb, p_353023_ -> p_353023_ instanceof ChainLeashable chainLeashable && pPredicate.test(chainLeashable)).stream().map(ChainLeashable.class::cast).toList(); } private static void setLeashedTo(E pEntity, Entity pLeashHolder, boolean pBroadcastPacket) { ChainLeashable.ChainData chainLeashable$chaindata = pEntity.getLeashData(); if (chainLeashable$chaindata == null) { chainLeashable$chaindata = new ChainLeashable.ChainData(pLeashHolder); pEntity.setLeashData(chainLeashable$chaindata); } else { chainLeashable$chaindata.setLeashHolder(pLeashHolder); } if (pBroadcastPacket && pEntity.level() instanceof ServerLevel serverlevel) { //TODO:自写Packet ClientboundSetEntityLinkPacket(pEntity, pLeashHolder) serverlevel.getChunkSource().broadcast(pEntity, new ClientboundSetEntityLinkPacket(pEntity, pLeashHolder)); } if (pEntity.isPassenger()) { pEntity.stopRiding(); } } default void dropLeash(boolean pBroadcastPacket, boolean pDropItem) { dropLeash((Entity & ChainLeashable)this, pBroadcastPacket, pDropItem); } private static void dropLeash(E pEntity, boolean pBroadcastPacket, boolean pDropItem) { ChainLeashable.ChainData chainLeashable$chaindata = pEntity.getLeashData(); if (chainLeashable$chaindata != null && chainLeashable$chaindata.leashHolder != null) { pEntity.setLeashData(null); if (!pEntity.level().isClientSide && pDropItem) { pEntity.spawnAtLocation(Items.CHAIN); } if (pBroadcastPacket && pEntity.level() instanceof ServerLevel serverlevel) { serverlevel.getChunkSource().broadcast(pEntity, new ClientboundSetEntityLinkPacket(pEntity, null)); } } } static void tickLeash(E pEntity) { ChainLeashable.ChainData chainLeashable$chaindata = pEntity.getLeashData(); if (chainLeashable$chaindata != null && chainLeashable$chaindata.delayedLeashInfo != null) { restoreLeashFromSave(pEntity, chainLeashable$chaindata); } if (chainLeashable$chaindata != null && chainLeashable$chaindata.leashHolder != null) { if (!pEntity.isAlive() || !chainLeashable$chaindata.leashHolder.isAlive()) { dropLeash(pEntity, true, true); } Entity entity = pEntity.getLeashHolder(); if (entity != null && entity.level() == pEntity.level()) { float f = pEntity.distanceTo(entity); if (!pEntity.handleLeashAtDistance(entity, f)) { return; } if ((double)f > 10.0) { pEntity.leashTooFarBehaviour(); } else if ((double)f > 6.0) { pEntity.elasticRangeLeashBehaviour(entity, f); pEntity.checkSlowFallDistance(); } else { pEntity.closeRangeLeashBehaviour(entity); } } } } private static void restoreLeashFromSave(E pEntity, ChainLeashable.ChainData pLeashData) { if (pLeashData.delayedLeashInfo != null && pEntity.level() instanceof ServerLevel serverlevel) { Optional optional1 = pLeashData.delayedLeashInfo.left(); Optional optional = pLeashData.delayedLeashInfo.right(); if (optional1.isPresent()) { Entity entity = serverlevel.getEntity(optional1.get()); if (entity != null) { setLeashedTo(pEntity, entity, true); return; } } else if (optional.isPresent()) { setLeashedTo(pEntity, LeashFenceKnotEntity.getOrCreateKnot(serverlevel, optional.get()), true); return; } if (pEntity.tickCount > 100) { pEntity.spawnAtLocation(Items.LEAD); pEntity.setLeashData(null); } } } final class ChainData { public int delayedLeashHolderId; @Nullable public Entity leashHolder; @Nullable public Either delayedLeashInfo; ChainData(@org.jetbrains.annotations.Nullable Either pDelayedLeashInfo) { this.delayedLeashInfo = pDelayedLeashInfo; } public ChainData(@org.jetbrains.annotations.Nullable Entity pLeashHolder) { this.leashHolder = pLeashHolder; } ChainData(int pDelayedLeashInfoId) { this.delayedLeashHolderId = pDelayedLeashInfoId; } public void setLeashHolder(Entity pLeashHolder) { this.leashHolder = pLeashHolder; this.delayedLeashInfo = null; this.delayedLeashHolderId = 0; } } }