Skip to content

Commit aa4601a

Browse files
XxFran10xXclauderyanbarlow97
authored
fix: keep only Cooking-owned horses and explain claiming (#57)
* fix: keep only Cooking-owned horses and explain claiming Revert the #56 load exemption: vanilla-tamed, named or saddled horses without a Cooking owner bypassed the max-animals cap. Unowned horses are removed on chunk load again, apart from enrolled wild mounts (unchanged). Players now get a hint when they tame or ride an unclaimed animal from the remove-unowned list: it will disappear unless claimed with a named Ownership Token, plus their owned/cap count or how to free a slot. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * fix: only warn about animals the cleanup would remove Riding enrolls horses, donkeys, mules and camels, and enrolled mounts survive the chunk-load cleanup without an owner, so the mount hint told players a horse would vanish when it would not. needsClaim now uses the cleanup's own rule. The tame hint no longer says to tame an animal that was just tamed (EntityTameEvent fires before taming). Wolves, cats and ocelots cannot be claimed, so they are no longer sent to the Ownership Token. The release instructions name the inspect menu's Remove ownership button, and removal is described as happening when the area next loads. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> --------- Co-authored-by: XxFran10xX <318299142+XxFran10xX@users.noreply.github.com> Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Co-authored-by: Ryan Barlow <7389646+ryanbarlow97@users.noreply.github.com>
1 parent f0f49d8 commit aa4601a

6 files changed

Lines changed: 320 additions & 126 deletions

File tree

Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
1+
package net.tfminecraft.cooking.husbandry;
2+
3+
import java.util.List;
4+
import java.util.Locale;
5+
import java.util.Map;
6+
import java.util.UUID;
7+
import java.util.concurrent.ConcurrentHashMap;
8+
9+
import org.bukkit.entity.Entity;
10+
import org.bukkit.entity.Player;
11+
import org.bukkit.entity.Tameable;
12+
13+
/** Tells players that animals they tame or ride vanish unless claimed in Cooking. */
14+
public final class HusbandryClaimHint {
15+
16+
static final long COOLDOWN_MILLIS = 60_000L;
17+
18+
private static final Map<UUID, Long> LAST_SENT = new ConcurrentHashMap<>();
19+
20+
private HusbandryClaimHint() {}
21+
22+
/** True when the chunk-load cleanup would remove this animal, using the same rule. */
23+
public static boolean needsClaim(Entity entity) {
24+
if (entity == null || !HusbandryConfig.isRemoveUnowned(entity.getType())) {
25+
return false;
26+
}
27+
UUID uuid = entity.getUniqueId();
28+
HusbandryRepository repository = HusbandryEntities.repository();
29+
boolean hasRow = HusbandryEntities.getLoaded(uuid).isPresent()
30+
|| (repository != null && repository.exists(uuid));
31+
return HusbandryMounts.shouldWipeUnowned(
32+
true,
33+
HusbandryOwnershipService.hasAnyOwner(uuid),
34+
hasRow,
35+
HusbandryMounts.hasConfiguredStats(entity));
36+
}
37+
38+
/** Sends the hint unless this player saw one within the cooldown. */
39+
public static void remind(Player player, Entity entity) {
40+
long now = System.currentTimeMillis();
41+
Long last = LAST_SENT.get(player.getUniqueId());
42+
if (last != null && now - last < COOLDOWN_MILLIS) {
43+
return;
44+
}
45+
send(player, entity, entity instanceof Tameable tameable && !tameable.isTamed());
46+
}
47+
48+
/**
49+
* {@code untamed} comes from the caller: during {@code EntityTameEvent} the animal is not tamed yet,
50+
* so reading it from the entity would tell a player who just tamed it to tame it first.
51+
*/
52+
public static void send(Player player, Entity entity, boolean untamed) {
53+
if (player == null || !needsClaim(entity)) {
54+
return;
55+
}
56+
LAST_SENT.put(player.getUniqueId(), System.currentTimeMillis());
57+
boolean claimable = HusbandryConfig.species(entity.getType()) != null;
58+
boolean staff = HusbandryOwnershipService.isStaff(player);
59+
HusbandryRepository repository = HusbandryEntities.repository();
60+
int owned = repository == null ? 0 : repository.countForPlayer(player.getUniqueId());
61+
for (String line : lines(HusbandryEntities.displayName(entity.getType()),
62+
claimable, untamed, owned, HusbandryConfig.maxAnimals(), staff)) {
63+
player.sendMessage(line);
64+
}
65+
}
66+
67+
public static void forget(UUID playerUuid) {
68+
LAST_SENT.remove(playerUuid);
69+
}
70+
71+
static List<String> lines(
72+
String species, boolean claimable, boolean untamed, int owned, int cap, boolean staff) {
73+
String label = species == null || species.isBlank() ? "animal" : species.toLowerCase(Locale.ROOT);
74+
String warning = "§eThis " + label + " is not claimed. Unclaimed animals disappear the next time"
75+
+ " their area loads.";
76+
if (!claimable) {
77+
return List.of(warning, "§7This kind of animal cannot be claimed, so it will not stay.");
78+
}
79+
String claim = (untamed ? "Tame it, then right-click" : "Right-click")
80+
+ " it with an Ownership Token renamed on an anvil";
81+
if (staff) {
82+
return List.of(warning,
83+
"§7" + claim + ". §8(staff: no animal limit)");
84+
}
85+
if (owned >= cap) {
86+
return List.of(warning,
87+
"§cYou already own " + owned + "/" + cap + " animals, so you cannot claim it.",
88+
"§7Release one first: shift-right-click it with an empty hand, then click Remove ownership."
89+
+ " See §f/animals§7.");
90+
}
91+
return List.of(warning,
92+
"§7" + claim + " to keep it.",
93+
"§7You own §f" + owned + "/" + cap + "§7 animals. See §f/animals§7.");
94+
}
95+
}

‎src/main/java/net/tfminecraft/cooking/husbandry/HusbandryLifecycleListener.java‎

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -155,11 +155,10 @@ static void handleLoad(Entity entity) {
155155
}
156156
UUID uuid = entity.getUniqueId();
157157
boolean hasRow = repository.exists(uuid);
158-
boolean playerKeptMount = HusbandryMounts.isPlayerKeptMount(entity);
159158

160159
if (HusbandryMounts.shouldWipeUnowned(
161160
HusbandryConfig.isRemoveUnowned(entity.getType()),
162-
HusbandryOwnershipService.hasAnyOwner(uuid) || playerKeptMount,
161+
HusbandryOwnershipService.hasAnyOwner(uuid),
163162
hasRow,
164163
HusbandryMounts.hasConfiguredStats(entity))) {
165164
if (hasRow) {
@@ -170,9 +169,6 @@ static void handleLoad(Entity entity) {
170169
return;
171170
}
172171
if (!hasRow) {
173-
if (playerKeptMount) {
174-
HusbandryEntities.applyPersistFlags(living);
175-
}
176172
return;
177173
}
178174

‎src/main/java/net/tfminecraft/cooking/husbandry/HusbandryMountListener.java‎

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,11 @@
44
import org.bukkit.entity.LivingEntity;
55
import org.bukkit.entity.Player;
66
import org.bukkit.event.EventHandler;
7+
import org.bukkit.event.EventPriority;
78
import org.bukkit.event.Listener;
89
import org.bukkit.event.entity.EntityMountEvent;
10+
import org.bukkit.event.entity.EntityTameEvent;
11+
import org.bukkit.event.player.PlayerQuitEvent;
912

1013
public final class HusbandryMountListener implements Listener {
1114

@@ -25,6 +28,7 @@ public void onMount(EntityMountEvent event) {
2528
}
2629
}
2730
if (!HusbandryOwnershipService.hasAnyOwner(mount.getUniqueId())) {
31+
HusbandryClaimHint.remind(player, mount);
2832
return;
2933
}
3034
if (HusbandryOwnershipService.isOwner(player, mount.getUniqueId())
@@ -34,4 +38,17 @@ public void onMount(EntityMountEvent event) {
3438
event.setCancelled(true);
3539
player.sendMessage("§cThis is not your animal.");
3640
}
41+
42+
@EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true)
43+
public void onTame(EntityTameEvent event) {
44+
if (event.getOwner() instanceof Player player) {
45+
// The event fires before the animal is tamed, so it cannot report itself as tamed yet.
46+
HusbandryClaimHint.send(player, event.getEntity(), false);
47+
}
48+
}
49+
50+
@EventHandler
51+
public void onQuit(PlayerQuitEvent event) {
52+
HusbandryClaimHint.forget(event.getPlayer().getUniqueId());
53+
}
3754
}

‎src/main/java/net/tfminecraft/cooking/husbandry/HusbandryMounts.java‎

Lines changed: 0 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,6 @@
88
import org.bukkit.entity.Entity;
99
import org.bukkit.entity.EntityType;
1010
import org.bukkit.entity.LivingEntity;
11-
import org.bukkit.inventory.ItemStack;
1211

1312
public final class HusbandryMounts {
1413

@@ -22,18 +21,6 @@ public static boolean hasConfiguredStats(Entity entity) {
2221
return entity != null && HusbandryConfig.mountStats(entity.getType()) != null;
2322
}
2423

25-
/** Vanilla ownership and equipment survive even when Cooking has no record. */
26-
public static boolean isPlayerKeptMount(Entity entity) {
27-
if (!(entity instanceof AbstractHorse horse)) {
28-
return false;
29-
}
30-
if (horse.isTamed() || horse.customName() != null) {
31-
return true;
32-
}
33-
ItemStack saddle = horse.getInventory().getSaddle();
34-
return saddle != null && !saddle.getType().isAir();
35-
}
36-
3724
public static boolean shouldWipeUnowned(
3825
boolean removeUnownedType, boolean hasOwner, boolean hasRow, boolean configuredMount) {
3926
return removeUnownedType && !hasOwner && !(hasRow && configuredMount);

‎src/test/java/net/tfminecraft/cooking/husbandry/HusbandryMountPersistenceTest.java‎

Lines changed: 0 additions & 108 deletions
This file was deleted.

0 commit comments

Comments
 (0)