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
4 changes: 4 additions & 0 deletions src/main/java/net/tfminecraft/cooking/Cooking.java
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
import net.tfminecraft.cooking.farming.FarmHarvestListener;
import net.tfminecraft.cooking.farming.FarmTrampleListener;
import net.tfminecraft.cooking.farming.FarmingLoader;
import net.tfminecraft.cooking.husbandry.HusbandryAnimalsCommand;
import net.tfminecraft.cooking.husbandry.HusbandryBreedListener;
import net.tfminecraft.cooking.husbandry.HusbandryCareListener;
import net.tfminecraft.cooking.husbandry.HusbandryDamageListener;
Expand Down Expand Up @@ -154,6 +155,9 @@ public void onEnable() {

getCommand("cooking").setExecutor(commands);
getCommand("cooking").setTabCompleter(commands);
HusbandryAnimalsCommand animals = new HusbandryAnimalsCommand();
getCommand("animals").setExecutor(animals);
getCommand("animals").setTabCompleter(animals);
if (ItemScanService.get() != null) {
ItemScanService.get().subscribe(tagManager);
ItemScanService.get().subscribe(legacyFishScan);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,10 @@ public final class HusbandryAnimal {
private long careUpRemainderSeconds;
private long careDownRemainderSeconds;
private String statsRevision;
private String world;
private Integer x;
private Integer y;
private Integer z;

public HusbandryAnimal(UUID uuid, String type, String name) {
this.uuid = uuid;
Expand Down Expand Up @@ -215,6 +219,52 @@ public void setStatsRevision(String statsRevision) {
this.statsRevision = statsRevision.trim();
}

public String world() {
return world;
}

public Integer x() {
return x;
}

public Integer y() {
return y;
}

public Integer z() {
return z;
}

public boolean hasLocation() {
return world != null && !world.isBlank() && x != null && y != null && z != null;
}

public void setLastLocation(String world, int x, int y, int z) {
if (world == null || world.isBlank()) {
clearLocation();
return;
}
this.world = world.trim();
this.x = x;
this.y = y;
this.z = z;
}

public void setStoredLocation(String world, Integer x, Integer y, Integer z) {
if (world == null || world.isBlank() || x == null || y == null || z == null) {
clearLocation();
return;
}
setLastLocation(world, x, y, z);
}

public void clearLocation() {
this.world = null;
this.x = null;
this.y = null;
this.z = null;
}

private static String sanitizeName(String name) {
if (name == null || name.isBlank() || "???".equals(name.trim())) {
return "";
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
package net.tfminecraft.cooking.husbandry;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Locale;
import java.util.UUID;

import org.bukkit.Bukkit;
import org.bukkit.OfflinePlayer;
import org.bukkit.command.Command;
import org.bukkit.command.CommandExecutor;
import org.bukkit.command.CommandSender;
import org.bukkit.command.TabCompleter;
import org.bukkit.entity.Entity;
import org.bukkit.entity.Player;

import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.format.NamedTextColor;
import net.tfminecraft.cooking.Cooking;

public final class HusbandryAnimalsCommand implements CommandExecutor, TabCompleter {

@Override
public boolean onCommand(CommandSender sender, Command command, String label, String[] args) {
HusbandryRepository repository = Cooking.plugin == null ? null : Cooking.plugin.getHusbandryRepository();
if (repository == null) {
sender.sendMessage(Component.text("Animal records are not available right now.", NamedTextColor.RED));
return true;
}
if (args.length > 0 && !sender.hasPermission("cooking.admin")) {
sender.sendMessage(Component.text("You can only list your own animals.", NamedTextColor.RED));
return true;
}
boolean self = args.length == 0;
UUID targetId;
String ownerName;
if (self) {
if (!(sender instanceof Player player)) {
sender.sendMessage(Component.text("Usage: /" + label + " <player>", NamedTextColor.RED));
return true;
}
targetId = player.getUniqueId();
ownerName = player.getName();
} else {
OfflinePlayer offline = Bukkit.getOfflinePlayerIfCached(args[0]);
if (offline == null) {
sender.sendMessage(Component.text("No player by that name has joined.", NamedTextColor.RED));
return true;
}
targetId = offline.getUniqueId();
ownerName = offline.getName() == null ? args[0] : offline.getName();
}
List<HusbandryOwned> rows = new ArrayList<>();
for (HusbandryOwned owned : repository.listForPlayer(targetId)) {
if (owned.animal() == null) {
continue;
}
HusbandryAnimal animal = HusbandryEntities.getLoaded(owned.animal().uuid()).orElse(owned.animal());
Entity live = Bukkit.getEntity(animal.uuid());
if (live != null) {
HusbandryLocation.remember(animal, live);
}
rows.add(new HusbandryOwned(animal, owned.role()));
}
for (Component line : HusbandryRoster.render(
ownerName, self, rows, HusbandryConfig.maxAnimals(), System.currentTimeMillis())) {
sender.sendMessage(line);
}
return true;
}

@Override
public List<String> onTabComplete(CommandSender sender, Command command, String alias, String[] args) {
if (!sender.hasPermission("cooking.admin") || args.length != 1) {
return Collections.emptyList();
}
String prefix = args[0].toLowerCase(Locale.ROOT);
List<String> names = new ArrayList<>();
for (Player player : Bukkit.getOnlinePlayers()) {
if (player.getName().toLowerCase(Locale.ROOT).startsWith(prefix)) {
names.add(player.getName());
}
}
names.sort(String.CASE_INSENSITIVE_ORDER);
return names;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,10 @@ public static void flushLoadedForDisable() {
long now = System.currentTimeMillis();
List<HusbandryAnimal> toSave = new ArrayList<>(snapshot.size());
for (HusbandryAnimal animal : snapshot) {
Entity entity = Bukkit.getEntity(animal.uuid());
if (entity != null) {
HusbandryLocation.remember(animal, entity);
}
animal.setUnloadedAt(now);
toSave.add(animal);
}
Expand Down Expand Up @@ -147,6 +151,7 @@ static void handleLoad(Entity entity) {
}
HusbandryAnimal animal = stored.get();
long now = System.currentTimeMillis();
HusbandryLocation.remember(animal, living);
HusbandrySimulator.catchUp(animal, now, java.util.concurrent.ThreadLocalRandom.current());
HusbandryGrowth.applyMaturity(living, animal, now);
HusbandryMounts.applyStats(living, animal);
Expand All @@ -172,6 +177,7 @@ static void handleUnload(Entity entity) {
return;
}
HusbandryAnimal animal = stored.get();
HusbandryLocation.remember(animal, entity);
animal.setUnloadedAt(System.currentTimeMillis());
repository.upsertAnimal(animal);
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
package net.tfminecraft.cooking.husbandry;

import org.bukkit.Location;
import org.bukkit.World;
import org.bukkit.entity.Entity;

public final class HusbandryLocation {

private HusbandryLocation() {}

public static void remember(HusbandryAnimal animal, Entity entity) {
if (animal == null || entity == null) {
return;
}
World world = entity.getWorld();
if (world == null) {
return;
}
Location location = entity.getLocation();
animal.setLastLocation(world.getName(), location.getBlockX(), location.getBlockY(), location.getBlockZ());
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
package net.tfminecraft.cooking.husbandry;

import java.util.Locale;

public record HusbandryOwned(HusbandryAnimal animal, String role) {

public HusbandryOwned {
if (role == null || role.isBlank()) {
role = "owner";
} else {
role = role.toLowerCase(Locale.ROOT);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -67,8 +67,9 @@ INSERT INTO animals (
hungry_since, dirty_since, last_processed_at, unloaded_at,
affliction_elapsed, affliction_at, last_milk_at, wool_ready_at,
neutered, loaded_visit_start, mature_at, shed_ready_at, egg_ready_at,
care_up_remainder, care_down_remainder, stats_revision
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
care_up_remainder, care_down_remainder, stats_revision,
world, x, y, z
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
ON CONFLICT(uuid) DO UPDATE SET
type = excluded.type,
name = excluded.name,
Expand All @@ -90,7 +91,11 @@ ON CONFLICT(uuid) DO UPDATE SET
egg_ready_at = excluded.egg_ready_at,
care_up_remainder = excluded.care_up_remainder,
care_down_remainder = excluded.care_down_remainder,
stats_revision = excluded.stats_revision
stats_revision = excluded.stats_revision,
world = excluded.world,
x = excluded.x,
y = excluded.y,
z = excluded.z
""";

private static final String SELECT_ANIMAL = "SELECT * FROM animals WHERE uuid = ?";
Expand Down Expand Up @@ -180,6 +185,21 @@ private void migrateSchema() {
}
database.execute("PRAGMA user_version = 7");
}
if (version < 8) {
if (!hasColumn("animals", "world")) {
database.execute("ALTER TABLE animals ADD COLUMN world TEXT");
}
if (!hasColumn("animals", "x")) {
database.execute("ALTER TABLE animals ADD COLUMN x INTEGER");
}
if (!hasColumn("animals", "y")) {
database.execute("ALTER TABLE animals ADD COLUMN y INTEGER");
}
if (!hasColumn("animals", "z")) {
database.execute("ALTER TABLE animals ADD COLUMN z INTEGER");
}
database.execute("PRAGMA user_version = 8");
}
}

private boolean hasColumn(String table, String column) {
Expand Down Expand Up @@ -310,6 +330,21 @@ public List<HusbandryOwner> listOwners(UUID animalUuid) {
animalUuid.toString());
}

public List<HusbandryOwned> listForPlayer(UUID playerUuid) {
if (playerUuid == null) {
return List.of();
}
return queryList(
"""
SELECT a.*, o.role AS owner_role
FROM animals a
INNER JOIN owners o ON o.animal_uuid = a.uuid
WHERE o.player_uuid = ?
""",
result -> new HusbandryOwned(mapAnimal(result), result.getString("owner_role")),
playerUuid.toString());
}

public void checkpointWal(boolean truncate) {
String mode = truncate ? "TRUNCATE" : "PASSIVE";
database.execute("PRAGMA wal_checkpoint(" + mode + ")");
Expand Down Expand Up @@ -355,7 +390,11 @@ private static void bindAnimal(PreparedStatement statement, HusbandryAnimal anim
animal.eggReadyAt(),
animal.careUpRemainderSeconds(),
animal.careDownRemainderSeconds(),
blankToNull(animal.statsRevision()));
blankToNull(animal.statsRevision()),
animal.hasLocation() ? animal.world() : null,
animal.hasLocation() ? animal.x() : null,
animal.hasLocation() ? animal.y() : null,
animal.hasLocation() ? animal.z() : null);
}

private static HusbandryAnimal mapAnimal(ResultSet result) throws SQLException {
Expand All @@ -382,6 +421,11 @@ private static HusbandryAnimal mapAnimal(ResultSet result) throws SQLException {
animal.setCareUpRemainderSeconds(intOrZero(result, "care_up_remainder"));
animal.setCareDownRemainderSeconds(intOrZero(result, "care_down_remainder"));
animal.setStatsRevision(nullableString(result, "stats_revision"));
animal.setStoredLocation(
nullableString(result, "world"),
nullableInt(result, "x"),
nullableInt(result, "y"),
nullableInt(result, "z"));
return animal;
}

Expand Down Expand Up @@ -409,6 +453,15 @@ private static String nullableString(ResultSet result, String column) throws SQL
}
}

private static Integer nullableInt(ResultSet result, String column) throws SQLException {
try {
int value = result.getInt(column);
return result.wasNull() ? null : value;
} catch (SQLException ex) {
return null;
}
}

private static int intOrZero(ResultSet result, String column) throws SQLException {
try {
int value = result.getInt(column);
Expand Down
Loading