Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
package net.tfminecraft.cooking.husbandry;

import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.UUID;
import java.util.concurrent.ConcurrentHashMap;

import org.bukkit.entity.Entity;
import org.bukkit.entity.Player;
import org.bukkit.entity.Tameable;

/** Tells players that animals they tame or ride vanish unless claimed in Cooking. */
public final class HusbandryClaimHint {

static final long COOLDOWN_MILLIS = 60_000L;

private static final Map<UUID, Long> LAST_SENT = new ConcurrentHashMap<>();

private HusbandryClaimHint() {}

/** True when the chunk-load cleanup would remove this animal, using the same rule. */
public static boolean needsClaim(Entity entity) {
if (entity == null || !HusbandryConfig.isRemoveUnowned(entity.getType())) {
return false;
}
UUID uuid = entity.getUniqueId();
HusbandryRepository repository = HusbandryEntities.repository();
boolean hasRow = HusbandryEntities.getLoaded(uuid).isPresent()
|| (repository != null && repository.exists(uuid));
return HusbandryMounts.shouldWipeUnowned(
true,
HusbandryOwnershipService.hasAnyOwner(uuid),
hasRow,
HusbandryMounts.hasConfiguredStats(entity));
}

/** Sends the hint unless this player saw one within the cooldown. */
public static void remind(Player player, Entity entity) {
long now = System.currentTimeMillis();
Long last = LAST_SENT.get(player.getUniqueId());
if (last != null && now - last < COOLDOWN_MILLIS) {
return;
}
send(player, entity, entity instanceof Tameable tameable && !tameable.isTamed());
}

/**
* {@code untamed} comes from the caller: during {@code EntityTameEvent} the animal is not tamed yet,
* so reading it from the entity would tell a player who just tamed it to tame it first.
*/
public static void send(Player player, Entity entity, boolean untamed) {
if (player == null || !needsClaim(entity)) {
return;
}
LAST_SENT.put(player.getUniqueId(), System.currentTimeMillis());
boolean claimable = HusbandryConfig.species(entity.getType()) != null;
boolean staff = HusbandryOwnershipService.isStaff(player);
HusbandryRepository repository = HusbandryEntities.repository();
int owned = repository == null ? 0 : repository.countForPlayer(player.getUniqueId());
for (String line : lines(HusbandryEntities.displayName(entity.getType()),
claimable, untamed, owned, HusbandryConfig.maxAnimals(), staff)) {
player.sendMessage(line);
}
}

public static void forget(UUID playerUuid) {
LAST_SENT.remove(playerUuid);
}

static List<String> lines(
String species, boolean claimable, boolean untamed, int owned, int cap, boolean staff) {
String label = species == null || species.isBlank() ? "animal" : species.toLowerCase(Locale.ROOT);
String warning = "§eThis " + label + " is not claimed. Unclaimed animals disappear the next time"
+ " their area loads.";
if (!claimable) {
return List.of(warning, "§7This kind of animal cannot be claimed, so it will not stay.");
}
String claim = (untamed ? "Tame it, then right-click" : "Right-click")
+ " it with an Ownership Token renamed on an anvil";
if (staff) {
return List.of(warning,
"§7" + claim + ". §8(staff: no animal limit)");
}
if (owned >= cap) {
return List.of(warning,
"§cYou already own " + owned + "/" + cap + " animals, so you cannot claim it.",
"§7Release one first: shift-right-click it with an empty hand, then click Remove ownership."
+ " See §f/animals§7.");
}
return List.of(warning,
"§7" + claim + " to keep it.",
"§7You own §f" + owned + "/" + cap + "§7 animals. See §f/animals§7.");
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -125,11 +125,10 @@ static void handleLoad(Entity entity) {
}
UUID uuid = entity.getUniqueId();
boolean hasRow = repository.exists(uuid);
boolean playerKeptMount = HusbandryMounts.isPlayerKeptMount(entity);

if (HusbandryMounts.shouldWipeUnowned(
HusbandryConfig.isRemoveUnowned(entity.getType()),
HusbandryOwnershipService.hasAnyOwner(uuid) || playerKeptMount,
HusbandryOwnershipService.hasAnyOwner(uuid),
hasRow,
HusbandryMounts.hasConfiguredStats(entity))) {
if (hasRow) {
Expand All @@ -140,9 +139,6 @@ static void handleLoad(Entity entity) {
return;
}
if (!hasRow) {
if (playerKeptMount) {
HusbandryEntities.applyPersistFlags(living);
}
return;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,11 @@
import org.bukkit.entity.LivingEntity;
import org.bukkit.entity.Player;
import org.bukkit.event.EventHandler;
import org.bukkit.event.EventPriority;
import org.bukkit.event.Listener;
import org.bukkit.event.entity.EntityMountEvent;
import org.bukkit.event.entity.EntityTameEvent;
import org.bukkit.event.player.PlayerQuitEvent;

public final class HusbandryMountListener implements Listener {

Expand All @@ -25,6 +28,7 @@ public void onMount(EntityMountEvent event) {
}
}
if (!HusbandryOwnershipService.hasAnyOwner(mount.getUniqueId())) {
HusbandryClaimHint.remind(player, mount);
return;
}
if (HusbandryOwnershipService.isOwner(player, mount.getUniqueId())
Expand All @@ -34,4 +38,17 @@ public void onMount(EntityMountEvent event) {
event.setCancelled(true);
player.sendMessage("§cThis is not your animal.");
}

@EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true)
public void onTame(EntityTameEvent event) {
if (event.getOwner() instanceof Player player) {
// The event fires before the animal is tamed, so it cannot report itself as tamed yet.
HusbandryClaimHint.send(player, event.getEntity(), false);
}
}

@EventHandler
public void onQuit(PlayerQuitEvent event) {
HusbandryClaimHint.forget(event.getPlayer().getUniqueId());
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@
import org.bukkit.entity.Entity;
import org.bukkit.entity.EntityType;
import org.bukkit.entity.LivingEntity;
import org.bukkit.inventory.ItemStack;

public final class HusbandryMounts {

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

/** Vanilla ownership and equipment survive even when Cooking has no record. */
public static boolean isPlayerKeptMount(Entity entity) {
if (!(entity instanceof AbstractHorse horse)) {
return false;
}
if (horse.isTamed() || horse.customName() != null) {
return true;
}
ItemStack saddle = horse.getInventory().getSaddle();
return saddle != null && !saddle.getType().isAir();
}

public static boolean shouldWipeUnowned(
boolean removeUnownedType, boolean hasOwner, boolean hasRow, boolean configuredMount) {
return removeUnownedType && !hasOwner && !(hasRow && configuredMount);
Expand Down

This file was deleted.

Loading