EpicKnarvik97 3ed3c99c15
All checks were successful
EpicKnarvik97/Blacksmith/pipeline/head This commit looks good
Speeds up smith presets by using caching and sets
2024-05-06 12:56:22 +02:00

300 lines
10 KiB
Java

package net.knarcraft.blacksmith.util;
import net.knarcraft.blacksmith.BlacksmithPlugin;
import net.knarcraft.blacksmith.config.SmithPreset;
import org.bukkit.Material;
import org.bukkit.Server;
import org.bukkit.inventory.CraftingRecipe;
import org.bukkit.inventory.Inventory;
import org.bukkit.inventory.ItemStack;
import org.bukkit.inventory.Recipe;
import org.bukkit.inventory.meta.Damageable;
import org.bukkit.inventory.meta.ItemMeta;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.logging.Level;
/**
* A helper class for getting information about items
*/
public final class ItemHelper {
private ItemHelper() {
}
/**
* Gets whether the given item is repairable
*
* @param item <p>The item to check</p>
* @return <p>True if the item is repairable</p>
*/
public static boolean isRepairable(@NotNull ItemStack item) {
return item.getItemMeta() instanceof Damageable && getMaxDurability(item) > 0;
}
/**
* Gets the max durability of an item
*
* @param itemStack <p>The item to get the durability of</p>
* @return <p>The max durability of the item</p>
*/
public static short getMaxDurability(@NotNull ItemStack itemStack) {
if (itemStack.getItemMeta() instanceof Damageable) {
return itemStack.getType().getMaxDurability();
} else {
return 0;
}
}
/**
* Gets the current durability of the given item
*
* @param itemStack <p>The item to get the durability of</p>
* @return <p>The durability of the item</p>
*/
public static short getDurability(@NotNull ItemStack itemStack) {
if (itemStack.getItemMeta() instanceof Damageable damageable) {
int maxDurability = getMaxDurability(itemStack);
return (short) (maxDurability - damageable.getDamage());
} else {
return 0;
}
}
/**
* Gets the damage done to the given item
*
* @param itemStack <p>The damage done to the item</p>
* @return <p>The damage done to the item</p>
*/
public static short getDamage(@NotNull ItemStack itemStack) {
if (itemStack.getItemMeta() instanceof Damageable damageable) {
return (short) damageable.getDamage();
} else {
return 0;
}
}
/**
* Updates the damage done to an item
*
* @param item <p>The item to update damage for</p>
* @param newDamage <p>The new damage done</p>
* @return <p>True if the damage was updated. False if not damageable.</p>
*/
public static boolean updateDamage(@NotNull ItemStack item, int newDamage) {
ItemMeta meta = item.getItemMeta();
if (!(meta instanceof Damageable damageable)) {
return false;
}
damageable.setDamage(newDamage);
item.setItemMeta(meta);
return true;
}
/**
* Gets a complete list of all reforge-able materials
*
* <p>Note: As this loops through all materials, the result should be cached</p>
*
* @return <p>A complete list of reforge-able materials</p>
*/
public static @NotNull Set<Material> getAllReforgeAbleMaterials() {
Set<Material> reforgeAbleMaterials = new HashSet<>();
for (Material material : Material.values()) {
ItemStack item = new ItemStack(material);
if (isRepairable(item)) {
reforgeAbleMaterials.add(material);
}
}
return reforgeAbleMaterials;
}
/**
* Checks whether the given material is an anvil
*
* @param material <p>The material to check</p>
* @param requireDamaged <p>Whether only a damaged anvil should count</p>
* @return <p>True if the given material is an anvil</p>
*/
public static boolean isAnvil(@NotNull Material material, boolean requireDamaged) {
boolean isDamagedAnvil = material == Material.CHIPPED_ANVIL || material == Material.DAMAGED_ANVIL;
boolean isAnvil = isDamagedAnvil || material == Material.ANVIL;
return (requireDamaged && isDamagedAnvil) || (!requireDamaged && isAnvil);
}
/**
* Checks whether the given inventory is able to fit the given item
*
* @param inventory <p>The inventory to check</p>
* @param item <p>The item to check</p>
* @return <p>True if the inventory can fit the item</p>
*/
public static boolean canFitItem(@NotNull Inventory inventory, @NotNull ItemStack item) {
// If a slot is available, there is no problem
if (inventory.firstEmpty() != -1) {
return true;
}
// If the inventory doesn't contain the correct type of item, stacking is impossible
if (!inventory.contains(item.getType())) {
return false;
}
// Check if the item stack can fit in the inventory if stacked with existing items
int availableSlots = 0;
for (ItemStack itemStack : inventory.getStorageContents()) {
ItemMeta itemMeta = itemStack.getItemMeta();
ItemMeta targetMeta = item.getItemMeta();
// Skip items of a different type, or with metadata that would prevent stacking
if (itemStack.getType() != item.getType() ||
(itemMeta != null && targetMeta != null && !itemMeta.equals(targetMeta))) {
continue;
}
availableSlots += itemStack.getMaxStackSize() - itemStack.getAmount();
if (availableSlots < item.getAmount()) {
return true;
}
}
return false;
}
/**
* Checks whether the given item can be salvaged, assuming no restrictions apply
*
* @param server <p>The server to get recipes from</p>
* @param item <p>The item to check</p>
* @return <p>True if the item can be salvaged</p>
*/
public static boolean isSalvageable(@NotNull Server server, @NotNull ItemStack item) {
for (Recipe recipe : server.getRecipesFor(new ItemStack(item.getType(), item.getAmount()))) {
// Only crafting recipes are allowed.
if ((recipe instanceof CraftingRecipe) && item.getAmount() >= recipe.getResult().getAmount()) {
return true;
}
}
return false;
}
/**
* Gets the amount of an item that's required to salvage that item
*
* @param server <p>The server to get recipes from</p>
* @param item <p>The item to check</p>
* @return <p>The number of items required for salvage, or -1 if the recipe was not found</p>
*/
public static int getRequiredAmountForSalvage(@NotNull Server server, @NotNull ItemStack item) {
for (Recipe recipe : server.getRecipesFor(new ItemStack(item.getType(), item.getAmount()))) {
// Only crafting recipes are allowed.
if (recipe instanceof CraftingRecipe) {
return recipe.getResult().getAmount();
}
}
return -1;
}
/**
* Gets all materials matching the given material wildcard
*
* @param materialName <p>The material name or material wildcard to match</p>
* @param extended <p>Whether to use an extended match, allowing any material</p>
* @return <p>The matched material(s)</p>
*/
public static @NotNull Set<Material> getWildcardMatch(@NotNull String materialName, boolean extended) {
String search = InputParsingHelper.regExIfy(materialName);
Set<Material> materials = new HashSet<>();
Set<Material> all;
if (extended) {
all = Set.of(Material.values());
} else {
all = ItemHelper.getAllReforgeAbleMaterials();
}
for (Material material : all) {
if (material.name().matches(search)) {
materials.add(material);
}
}
return materials;
}
/**
* Gets a list of the items described in the given item list
*
* @param itemList <p>The list of items defined by the user</p>
* @param requireRepairable <p>Whether a material must be repairable to be valid.</p>
* @return <p>The materials contained in the item list</p>
*/
public static List<Material> getItems(@Nullable List<String> itemList, boolean requireRepairable) {
List<Material> items = new ArrayList<>();
if (itemList == null) {
return null;
}
//Convert any presets with a list of materials
itemList = replacePresets(itemList);
Set<Material> blacklisted = new HashSet<>();
//Parse every material, and add to reforgeAble items
for (String item : itemList) {
//Ignore ",,"
if (InputParsingHelper.isEmpty(item) || item.matches("\\[]")) {
continue;
}
boolean blacklist = false;
if (item.startsWith("-")) {
blacklist = true;
item = item.substring(1);
}
Material material = InputParsingHelper.matchMaterial(item);
if (material != null && (!requireRepairable || ItemHelper.isRepairable(new ItemStack(material, 1)))) {
if (!blacklist) {
items.add(material);
} else {
blacklisted.add(material);
}
} else {
BlacksmithPlugin.getInstance().getLogger().log(Level.WARNING, "Unable to verify " + item +
" as a valid repairable item");
}
}
//Remove any blacklisted materials at the end to make sure order of arguments won't matter
items.removeAll(blacklisted);
return items;
}
/**
* Replaces smith presets in the given list of strings
*
* @param stringList <p>The value specified by a user</p>
* @return <p>The value with smith presets replaced</p>
*/
private static @NotNull List<String> replacePresets(@NotNull List<String> stringList) {
List<String> newStrings = new ArrayList<>();
for (String item : stringList) {
if (item == null) {
continue;
}
String replaced = SmithPreset.replacePreset(item);
if (!replaced.equals(item)) {
newStrings.addAll(List.of(replaced.split(",")));
} else {
newStrings.add(item);
}
}
return newStrings;
}
}