diff --git a/src/main/java/net/tfminecraft/cooking/husbandry/HusbandryCoOwnToken.java b/src/main/java/net/tfminecraft/cooking/husbandry/HusbandryCoOwnToken.java new file mode 100644 index 0000000..3ce523f --- /dev/null +++ b/src/main/java/net/tfminecraft/cooking/husbandry/HusbandryCoOwnToken.java @@ -0,0 +1,29 @@ +package net.tfminecraft.cooking.husbandry; + +import java.util.UUID; + +/** Decides when a right-click with a linked Co-Ownership Token accepts it. */ +final class HusbandryCoOwnToken { + + private HusbandryCoOwnToken() {} + + /** + * A token linked to someone else's animal is accepted by any right-click: the air, a block, an + * animal or a player. Clients follow an entity click with a use-item packet, so every path must + * agree or the player sees an error and then a success. Owners keep linking and giving. + */ + static boolean accepts(UUID linkedAnimal, boolean linkedHasOwners, boolean clickerOwnsLinked) { + return linkedAnimal != null && linkedHasOwners && !clickerOwnsLinked; + } + + static UUID parse(String linked) { + if (linked == null || linked.isBlank()) { + return null; + } + try { + return UUID.fromString(linked); + } catch (IllegalArgumentException ignored) { + return null; + } + } +} diff --git a/src/main/java/net/tfminecraft/cooking/husbandry/HusbandryTamingListener.java b/src/main/java/net/tfminecraft/cooking/husbandry/HusbandryTamingListener.java index 2b4397c..4d395a7 100644 --- a/src/main/java/net/tfminecraft/cooking/husbandry/HusbandryTamingListener.java +++ b/src/main/java/net/tfminecraft/cooking/husbandry/HusbandryTamingListener.java @@ -47,7 +47,9 @@ public void onInteractEntity(PlayerInteractEntityEvent event) { if (HusbandryItems.matches(hand, HusbandryConfig.coOwnItem())) { event.setCancelled(true); - if (clicked instanceof Player target) { + if (acceptsLinkedToken(player, hand)) { + handleCoOwnOnPlayer(player, player, hand); + } else if (clicked instanceof Player target) { handleCoOwnOnPlayer(player, target, hand); } else if (clicked instanceof LivingEntity living) { handleCoOwnLink(player, living, hand); @@ -66,7 +68,7 @@ public void onInteractEntity(PlayerInteractEntityEvent event) { } @EventHandler - public void onInteractAir(PlayerInteractEvent event) { + public void onInteractUse(PlayerInteractEvent event) { if (event.getHand() != EquipmentSlot.HAND) { return; } @@ -74,20 +76,25 @@ public void onInteractAir(PlayerInteractEvent event) { && event.getAction() != org.bukkit.event.block.Action.RIGHT_CLICK_BLOCK) { return; } - if (event.getClickedBlock() != null) { - return; - } ItemStack hand = event.getItem(); if (!HusbandryItems.matches(hand, HusbandryConfig.coOwnItem())) { return; } - if (HusbandryItems.linkedAnimal(hand) == null) { + if (!acceptsLinkedToken(event.getPlayer(), hand)) { return; } event.setCancelled(true); handleCoOwnOnPlayer(event.getPlayer(), event.getPlayer(), hand); } + private static boolean acceptsLinkedToken(Player player, ItemStack hand) { + UUID linked = HusbandryCoOwnToken.parse(HusbandryItems.linkedAnimal(hand)); + return HusbandryCoOwnToken.accepts( + linked, + HusbandryOwnershipService.hasAnyOwner(linked), + HusbandryOwnershipService.isOwner(player, linked)); + } + private static void handleTame(Player player, LivingEntity entity, ItemStack hand) { String name = HusbandryItems.tameName(hand); if (name == null) { diff --git a/src/test/java/net/tfminecraft/cooking/husbandry/HusbandryCoOwnTokenTest.java b/src/test/java/net/tfminecraft/cooking/husbandry/HusbandryCoOwnTokenTest.java new file mode 100644 index 0000000..6ff13e9 --- /dev/null +++ b/src/test/java/net/tfminecraft/cooking/husbandry/HusbandryCoOwnTokenTest.java @@ -0,0 +1,43 @@ +package net.tfminecraft.cooking.husbandry; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import java.util.UUID; + +import org.junit.jupiter.api.Test; + +class HusbandryCoOwnTokenTest { + + private static final UUID ANIMAL = UUID.randomUUID(); + + @Test + void anotherPlayerAcceptsATokenLinkedToAnOwnedAnimal() { + assertTrue(HusbandryCoOwnToken.accepts(ANIMAL, true, false)); + } + + @Test + void ownersKeepLinkingAndGivingInsteadOfAccepting() { + assertFalse(HusbandryCoOwnToken.accepts(ANIMAL, true, true)); + } + + @Test + void tokensForAnimalsWithoutOwnersAreNotAccepted() { + assertFalse(HusbandryCoOwnToken.accepts(ANIMAL, false, false)); + } + + @Test + void unlinkedTokensAreNotAccepted() { + assertFalse(HusbandryCoOwnToken.accepts(null, true, false)); + } + + @Test + void parsesOnlyValidLinks() { + assertEquals(ANIMAL, HusbandryCoOwnToken.parse(ANIMAL.toString())); + assertNull(HusbandryCoOwnToken.parse(null)); + assertNull(HusbandryCoOwnToken.parse(" ")); + assertNull(HusbandryCoOwnToken.parse("not-a-uuid")); + } +}