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
85 changes: 64 additions & 21 deletions src/main/java/net/tfminecraft/cooking/Cooking.java
Original file line number Diff line number Diff line change
@@ -1,9 +1,14 @@
package net.tfminecraft.cooking;

import java.io.File;
import java.util.List;
import java.util.Map;
import java.util.logging.Logger;

import org.bukkit.Bukkit;
import org.bukkit.command.Command;
import org.bukkit.command.CommandMap;
import org.bukkit.command.PluginCommand;
import org.bukkit.plugin.Plugin;
import org.bukkit.plugin.java.JavaPlugin;

Expand All @@ -23,6 +28,7 @@
import net.tfminecraft.cooking.husbandry.HusbandryAnimalsCommand;
import net.tfminecraft.cooking.husbandry.HusbandryBreedListener;
import net.tfminecraft.cooking.husbandry.HusbandryCareListener;
import net.tfminecraft.cooking.husbandry.HusbandryConfig;
import net.tfminecraft.cooking.husbandry.HusbandryDamageListener;
import net.tfminecraft.cooking.husbandry.HusbandryDeathListener;
import net.tfminecraft.cooking.husbandry.HusbandryHarvestListener;
Expand Down Expand Up @@ -107,6 +113,7 @@ public class Cooking extends JavaPlugin {
private final CropsLoader cropsLoader = new CropsLoader();
private final HusbandryLoader husbandryLoader = new HusbandryLoader();
private HusbandryRepository husbandryRepository;
private boolean husbandryEnabled;

private final TagManager tagManager = new TagManager();
private final LegacyFishScan legacyFishScan = new LegacyFishScan();
Expand Down Expand Up @@ -152,9 +159,14 @@ public void onEnable() {
createConfigs();
loadConfigs();
TLibs.getItemAPI().registerPathHandler("c", CookingPathHandler.INSTANCE);
openHusbandryDatabase();
HusbandryLifecycleListener.applyStatsRevision();
net.tfminecraft.cooking.husbandry.HusbandryIdentity.start(this, husbandryRepository);
husbandryEnabled = HusbandryConfig.enabled();
if (husbandryEnabled) {
openHusbandryDatabase();
HusbandryLifecycleListener.applyStatsRevision();
net.tfminecraft.cooking.husbandry.HusbandryIdentity.start(this, husbandryRepository);
} else {
getLogger().info("Husbandry is disabled in husbandry.yml; animals are left to other plugins.");
}
registerListeners();

cookingManager.start();
Expand All @@ -174,17 +186,23 @@ public void onEnable() {
SaturationGuard.start();
NutritionDrainTask.start();
Bukkit.getOnlinePlayers().forEach(NutritionLifecycleListener::initializeCurrent);
HusbandryLifecycleListener.resumeLoadedWorlds();
HusbandryTickTask.start();
HusbandryLocator.scanUnloaded();
if (husbandryEnabled) {
HusbandryLifecycleListener.resumeLoadedWorlds();
HusbandryTickTask.start();
HusbandryLocator.scanUnloaded();
}
MMOItemsSupport.claimFirstIfPresent(getServer().getPluginManager(), getLogger());
});

getCommand("cooking").setExecutor(commands);
getCommand("cooking").setTabCompleter(commands);
HusbandryAnimalsCommand animals = new HusbandryAnimalsCommand();
getCommand("animals").setExecutor(animals);
getCommand("animals").setTabCompleter(animals);
if (husbandryEnabled) {
HusbandryAnimalsCommand animals = new HusbandryAnimalsCommand();
getCommand("animals").setExecutor(animals);
getCommand("animals").setTabCompleter(animals);
} else {
releaseAnimalsCommand();
}
if (ItemScanService.get() != null) {
ItemScanService.get().subscribe(tagManager);
ItemScanService.get().subscribe(legacyFishScan);
Expand Down Expand Up @@ -248,8 +266,31 @@ public void reloadAll() {
craftingManager.rebuildStations();
NutritionDrainTask.stop();
NutritionDrainTask.start();
HusbandryTickTask.stop();
HusbandryTickTask.start();
if (HusbandryConfig.enabled() != husbandryEnabled) {
getLogger().warning("husbandry.yml enabled changed; restart the server to apply it.");
}
if (husbandryEnabled) {
HusbandryTickTask.stop();
HusbandryTickTask.start();
}
}

/** True when Cooking runs livestock; read once at startup from husbandry.yml {@code enabled}. */
public boolean isHusbandryEnabled() {
return husbandryEnabled;
}

/** Frees /animals and /livestock so the plugin that runs livestock can register them. */
private void releaseAnimalsCommand() {
PluginCommand animals = getCommand("animals");
CommandMap map = getServer().getCommandMap();
animals.unregister(map);
Map<String, Command> known = map.getKnownCommands();
for (String label : List.copyOf(known.keySet())) {
if (known.get(label) == animals) {
known.remove(label);
}
}
}

// ----------------------------------------------------------------------
Expand Down Expand Up @@ -279,16 +320,18 @@ public void registerListeners() {
CropCustomCropsBridge.tryRegister(this);
getServer().getPluginManager().registerEvents(new CustomFishingBridge(), this);
CustomFishingBridge.tryRegister(this);
getServer().getPluginManager().registerEvents(new HusbandryLifecycleListener(), this);
getServer().getPluginManager().registerEvents(new HusbandryCareListener(), this);
getServer().getPluginManager().registerEvents(new HusbandryTamingListener(), this);
getServer().getPluginManager().registerEvents(new HusbandryDeathListener(), this);
getServer().getPluginManager().registerEvents(new HusbandryHarvestListener(), this);
getServer().getPluginManager().registerEvents(new HusbandryBreedListener(), this);
getServer().getPluginManager().registerEvents(new HusbandryNeuterListener(), this);
getServer().getPluginManager().registerEvents(new HusbandryInspectListener(), this);
getServer().getPluginManager().registerEvents(new HusbandryMountListener(), this);
getServer().getPluginManager().registerEvents(new HusbandryDamageListener(), this);
if (husbandryEnabled) {
getServer().getPluginManager().registerEvents(new HusbandryLifecycleListener(), this);
getServer().getPluginManager().registerEvents(new HusbandryCareListener(), this);
getServer().getPluginManager().registerEvents(new HusbandryTamingListener(), this);
getServer().getPluginManager().registerEvents(new HusbandryDeathListener(), this);
getServer().getPluginManager().registerEvents(new HusbandryHarvestListener(), this);
getServer().getPluginManager().registerEvents(new HusbandryBreedListener(), this);
getServer().getPluginManager().registerEvents(new HusbandryNeuterListener(), this);
getServer().getPluginManager().registerEvents(new HusbandryInspectListener(), this);
getServer().getPluginManager().registerEvents(new HusbandryMountListener(), this);
getServer().getPluginManager().registerEvents(new HusbandryDamageListener(), this);
}
getServer().getPluginManager().registerEvents(new ConversionManager(), this);
getServer().getPluginManager().registerEvents(new BundleFoodStacker(), this);
getServer().getPluginManager().registerEvents(new RegenBlocker(), this);
Expand Down
111 changes: 111 additions & 0 deletions src/main/java/net/tfminecraft/cooking/api/CookingFoodApi.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
package net.tfminecraft.cooking.api;

import java.util.OptionalInt;

import org.bukkit.Material;
import org.bukkit.entity.EntityType;
import org.bukkit.entity.Player;
import org.bukkit.inventory.ItemStack;
import org.bukkit.inventory.meta.ItemMeta;
import org.bukkit.persistence.PersistentDataType;

import net.tfminecraft.cooking.carve.CarvableRoastUtils;
import net.tfminecraft.cooking.carve.CarveSequence;
import net.tfminecraft.cooking.item.FoodItem;
import net.tfminecraft.cooking.loader.CarveSequenceLoader;
import net.tfminecraft.cooking.utils.FoodParser;
import net.tfminecraft.cooking.utils.InventoryAdder;
import net.tfminecraft.cooking.utils.ItemBuilder;
import net.tfminecraft.cooking.utils.ItemUpdater;
import net.tfminecraft.cooking.utils.Keys;
import net.tfminecraft.cooking.utils.QualityUtils;

/**
* Food that other plugins hand out, such as animal products. Main thread only.
* Quality is the Cooking star rating and is clamped to 1-5.
*/
public final class CookingFoodApi {

private CookingFoodApi() {}

/** One food from a Cooking string such as {@code food(type=egg;origin=Egg)}, or null if it is not a food. */
public static ItemStack buildFood(String food, int quality) {
return build(food, quality, null, -1);
}

/**
* A roast from a Cooking string such as {@code meat(type=roast;origin=Beef)}, carved down to this many
* meat cuts before its closing bone. The carve sequence floor still applies. Foods without a carve
* sequence are returned whole. Null if the string is not a food.
*/
public static ItemStack buildRoast(String food, int quality, int meatCuts) {
return build(food, quality, null, Math.max(0, meatCuts));
}

/** A Cooking milk bucket from a goat, or from a cow for any other animal. Null if milk is not configured. */
public static ItemStack buildMilk(EntityType origin, int quality) {
return build(milkFood(origin), quality, new ItemStack(Material.MILK_BUCKET), -1);
}

/** The Cooking string {@link #buildMilk} uses for this animal. */
public static String milkFood(EntityType origin) {
return origin == EntityType.GOAT
? "food(type=milk_bucket;origin=Goat)"
: "food(type=milk_bucket;origin=Cow)";
}

/** Stars stored on a Cooking item, or empty when the item has no Cooking quality. */
public static OptionalInt qualityOf(ItemStack stack) {
if (stack == null || stack.getType().isAir() || !stack.hasItemMeta()) {
return OptionalInt.empty();
}
ItemMeta meta = stack.getItemMeta();
if (meta == null) {
return OptionalInt.empty();
}
Integer stored = meta.getPersistentDataContainer().get(Keys.QUALITY, PersistentDataType.INTEGER);
return stored == null ? OptionalInt.empty() : OptionalInt.of(QualityUtils.clamp(stored));
}

/** Adds an item the way Cooking stacks food. Returns what did not fit, or null. */
public static ItemStack addToInventory(Player player, ItemStack item) {
return InventoryAdder.addItem(player, item);
}

private static ItemStack build(String food, int quality, ItemStack base, int meatCuts) {
if (food == null || food.isBlank()) {
return null;
}
FoodParser.Result parsed = FoodParser.parse(food);
if (parsed == null || parsed.template == null) {
return null;
}
ItemStack stack = ItemBuilder.buildSingleWithQuality(parsed.template, base, QualityUtils.clamp(quality));
if (meatCuts < 0) {
return stack;
}
FoodItem item = FoodItem.fromItem(stack);
if (item == null) {
return stack;
}
CarvableRoastUtils.readCarveState(item, stack);
String seqId = item.getCarveSequencePdc() != null
? item.getCarveSequencePdc()
: item.getCarveSequenceId();
if (seqId == null) {
return stack;
}
CarveSequence seq = CarveSequenceLoader.get(seqId);
if (seq == null) {
return stack;
}
CarvableRoastUtils.RoastPortion portion = CarvableRoastUtils.portion(seq, meatCuts);
item.setCarveState(seqId, portion.nextIndex(), portion.remaining());
CarvableRoastUtils.writeCarveState(stack, item);
ItemStack updated = ItemUpdater.applyItemUpdate(stack, item, null);
if (updated != null && !updated.getType().isAir()) {
stack = updated;
}
return stack;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ public final class HusbandryConfig {
private static double geneticSlowdownDivisor = 1;
private static double careInfluence = 0.02;
private static String statsRevision = "1";
private static boolean enabled = true;
private static boolean restoreLostAnimals = true;
private static List<HusbandryAmountBand> amountBands = List.of();
private static double mountSpeedMinPct = 0.40;
Expand Down Expand Up @@ -158,6 +159,14 @@ public static void setStatsRevision(String revision) {
statsRevision = revision == null ? "" : revision.trim();
}

public static void setEnabled(boolean value) {
enabled = value;
}

public static boolean enabled() {
return enabled;
}

public static void setRestoreLostAnimals(boolean enabled) {
restoreLostAnimals = enabled;
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,14 +1,12 @@
package net.tfminecraft.cooking.husbandry;

import java.util.OptionalInt;
import java.util.UUID;
import java.util.concurrent.ConcurrentHashMap;

import org.bukkit.inventory.ItemStack;
import org.bukkit.inventory.meta.ItemMeta;
import org.bukkit.persistence.PersistentDataType;

import net.tfminecraft.cooking.utils.Keys;
import net.tfminecraft.cooking.utils.QualityUtils;
import net.tfminecraft.cooking.api.CookingFoodApi;

/**
* Breeding-food stars scale the gene bonus above the parent average.
Expand Down Expand Up @@ -54,11 +52,11 @@ public static double scaleOf(ItemStack stack) {
if (isUniversalFeed(stack)) {
return FULL_SCALE;
}
Integer stars = starsOrNull(stack);
if (stars == null) {
OptionalInt stars = CookingFoodApi.qualityOf(stack);
if (stars.isEmpty()) {
return FULL_SCALE;
}
return scaleForStars(stars);
return scaleForStars(stars.getAsInt());
}

public static void offer(UUID animalId, double scale, long nowMillis) {
Expand Down Expand Up @@ -132,20 +130,5 @@ private static boolean isUniversalFeed(ItemStack stack) {
}
}

private static Integer starsOrNull(ItemStack stack) {
if (stack == null || stack.getType().isAir() || !stack.hasItemMeta()) {
return null;
}
ItemMeta meta = stack.getItemMeta();
if (meta == null) {
return null;
}
Integer stored = meta.getPersistentDataContainer().get(Keys.QUALITY, PersistentDataType.INTEGER);
if (stored == null) {
return null;
}
return QualityUtils.clamp(stored);
}

private record TimedScale(double scale, long atMillis) {}
}
Loading
Loading