1
mirror of https://github.com/StarWishsama/Slimefun4.git synced 2024-09-19 19:25:48 +00:00

DistinctiveItem/isSimilar + Backpack canStack (#3417)

Co-authored-by: Daniel Walsh <walshydev@gmail.com>
Co-authored-by: poma123 <25465545+poma123@users.noreply.github.com>
This commit is contained in:
Sefiraat 2023-07-02 21:51:51 +01:00 committed by GitHub
parent 1beeb8fd8e
commit 934ab822a6
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 186 additions and 12 deletions

View File

@ -0,0 +1,33 @@
package io.github.thebusybiscuit.slimefun4.core.attributes;
import io.github.thebusybiscuit.slimefun4.api.items.SlimefunItemStack;
import io.github.thebusybiscuit.slimefun4.utils.SlimefunUtils;
import org.bukkit.inventory.meta.ItemMeta;
import io.github.thebusybiscuit.slimefun4.api.items.SlimefunItem;
import javax.annotation.Nonnull;
/**
* Implement this interface for any {@link SlimefunItem} to prevent
* cargo using only its ID when comparing. #canStack is used when
* comparing stacks
*
* @author Sefiraat
*/
public interface DistinctiveItem extends ItemAttribute {
/**
* This method is called by {@link SlimefunUtils#isItemSimilar} when two {@link SlimefunItemStack}
* IDs match on a DistinctiveItem and should return if the two items can stack
* with one another.
*
* @param itemMetaOne
* The {@link ItemMeta} of the first stack being compared.
* @param itemMetaTwo
* The {@link ItemMeta} of the second stack being compared.
*
* @return Whether the two {@link ItemMeta}s are stackable
*/
boolean canStack(@Nonnull ItemMeta itemMetaOne, @Nonnull ItemMeta itemMetaTwo);
}

View File

@ -6,16 +6,19 @@ import javax.annotation.ParametersAreNonnullByDefault;
import org.bukkit.inventory.Inventory;
import org.bukkit.inventory.ItemStack;
import org.bukkit.inventory.meta.ItemMeta;
import io.github.thebusybiscuit.slimefun4.api.items.ItemGroup;
import io.github.thebusybiscuit.slimefun4.api.items.SlimefunItem;
import io.github.thebusybiscuit.slimefun4.api.items.SlimefunItemStack;
import io.github.thebusybiscuit.slimefun4.api.player.PlayerBackpack;
import io.github.thebusybiscuit.slimefun4.api.recipes.RecipeType;
import io.github.thebusybiscuit.slimefun4.core.attributes.DistinctiveItem;
import io.github.thebusybiscuit.slimefun4.core.handlers.ItemUseHandler;
import io.github.thebusybiscuit.slimefun4.implementation.Slimefun;
import io.github.thebusybiscuit.slimefun4.implementation.items.SimpleSlimefunItem;
import io.github.thebusybiscuit.slimefun4.implementation.listeners.BackpackListener;
import io.github.thebusybiscuit.slimefun4.utils.SlimefunUtils;
import io.github.thebusybiscuit.slimefun4.utils.tags.SlimefunTag;
/**
@ -28,7 +31,7 @@ import io.github.thebusybiscuit.slimefun4.utils.tags.SlimefunTag;
* @see PlayerBackpack
*
*/
public class SlimefunBackpack extends SimpleSlimefunItem<ItemUseHandler> {
public class SlimefunBackpack extends SimpleSlimefunItem<ItemUseHandler> implements DistinctiveItem {
private final int size;
@ -82,4 +85,14 @@ public class SlimefunBackpack extends SimpleSlimefunItem<ItemUseHandler> {
};
}
@Override
public boolean canStack(@Nonnull ItemMeta itemMetaOne, @Nonnull ItemMeta itemMetaTwo) {
boolean hasLoreItem = itemMetaTwo.hasLore();
boolean hasLoreSfItem = itemMetaOne.hasLore();
if (hasLoreItem && hasLoreSfItem && SlimefunUtils.equalsLore(itemMetaTwo.getLore(), itemMetaOne.getLore())) {
return true;
}
return !hasLoreItem && !hasLoreSfItem;
}
}

View File

@ -90,9 +90,9 @@ public class EnhancedCraftingTable extends AbstractCraftingTable {
private boolean isCraftable(Inventory inv, ItemStack[] recipe) {
for (int j = 0; j < inv.getContents().length; j++) {
if (!SlimefunUtils.isItemSimilar(inv.getContents()[j], recipe[j], true)) {
if (!SlimefunUtils.isItemSimilar(inv.getContents()[j], recipe[j], true, true, false)) {
if (SlimefunItem.getByItem(recipe[j]) instanceof SlimefunBackpack) {
if (!SlimefunUtils.isItemSimilar(inv.getContents()[j], recipe[j], false)) {
if (!SlimefunUtils.isItemSimilar(inv.getContents()[j], recipe[j], false, true, false)) {
return false;
}
} else {

View File

@ -37,6 +37,7 @@ import io.github.thebusybiscuit.slimefun4.api.exceptions.PrematureCodeException;
import io.github.thebusybiscuit.slimefun4.api.items.ItemSpawnReason;
import io.github.thebusybiscuit.slimefun4.api.items.SlimefunItem;
import io.github.thebusybiscuit.slimefun4.api.items.SlimefunItemStack;
import io.github.thebusybiscuit.slimefun4.core.attributes.DistinctiveItem;
import io.github.thebusybiscuit.slimefun4.core.attributes.Radioactive;
import io.github.thebusybiscuit.slimefun4.core.attributes.Soulbound;
import io.github.thebusybiscuit.slimefun4.core.debug.Debug;
@ -260,7 +261,7 @@ public final class SlimefunUtils {
continue;
}
if (isItemSimilar(stack, item, checkLore, false)) {
if (isItemSimilar(stack, item, checkLore, false, true)) {
return true;
}
}
@ -268,11 +269,69 @@ public final class SlimefunUtils {
return false;
}
/**
* Compares two {@link ItemStack}s and returns if they are similar or not.
* Takes into account some shortcut checks specific to {@link SlimefunItem}s
* for performance.
* Will check for distintion of items by default and will also confirm the amount
* is the same.
* @see DistinctiveItem
*
* @param item
* The {@link ItemStack} being tested.
* @param sfitem
* The {@link ItemStack} that {@param item} is being compared against.
* @param checkLore
* Whether to include the current lore of either item in the comparison
*
* @return True if the given {@link ItemStack}s are similar under the given constraints
*/
public static boolean isItemSimilar(@Nullable ItemStack item, @Nullable ItemStack sfitem, boolean checkLore) {
return isItemSimilar(item, sfitem, checkLore, true);
return isItemSimilar(item, sfitem, checkLore, true, true);
}
/**
* Compares two {@link ItemStack}s and returns if they are similar or not.
* Takes into account some shortcut checks specific to {@link SlimefunItem}s
* for performance.
* Will check for distintion of items by default
* @see DistinctiveItem
*
* @param item
* The {@link ItemStack} being tested.
* @param sfitem
* The {@link ItemStack} that {@param item} is being compared against.
* @param checkLore
* Whether to include the current lore of either item in the comparison
* @param checkAmount
* Whether to include the item's amount(s) in the comparison
*
* @return True if the given {@link ItemStack}s are similar under the given constraints
*/
public static boolean isItemSimilar(@Nullable ItemStack item, @Nullable ItemStack sfitem, boolean checkLore, boolean checkAmount) {
return isItemSimilar(item, sfitem, checkLore, checkAmount, true);
}
/**
* Compares two {@link ItemStack}s and returns if they are similar or not.
* Takes into account some shortcut checks specific to {@link SlimefunItem}s
* for performance.
*
* @param item
* The {@link ItemStack} being tested.
* @param sfitem
* The {@link ItemStack} that {@param item} is being compared against.
* @param checkLore
* Whether to include the current lore of either item in the comparison
* @param checkAmount
* Whether to include the item's amount(s) in the comparison
* @param checkDistinction
* Whether to check for special distinctive properties of the items.
* @see DistinctiveItem
*
* @return True if the given {@link ItemStack}s are similar under the given constraints
*/
public static boolean isItemSimilar(@Nullable ItemStack item, @Nullable ItemStack sfitem, boolean checkLore, boolean checkAmount, boolean checkDistinction) {
if (item == null) {
return sfitem == null;
} else if (sfitem == null) {
@ -281,17 +340,42 @@ public final class SlimefunUtils {
return false;
} else if (checkAmount && item.getAmount() < sfitem.getAmount()) {
return false;
} else if (sfitem instanceof SlimefunItemStack && item instanceof SlimefunItemStack) {
return ((SlimefunItemStack) item).getItemId().equals(((SlimefunItemStack) sfitem).getItemId());
} else if (sfitem instanceof SlimefunItemStack stackOne && item instanceof SlimefunItemStack stackTwo) {
if (stackOne.getItemId().equals(stackTwo.getItemId())) {
/*
* PR #3417
*
* Some items can't rely on just IDs matching and will implement {@link DistinctiveItem}
* in which case we want to use the method provided to compare
*/
if (checkDistinction && stackOne instanceof DistinctiveItem distinctive && stackTwo instanceof DistinctiveItem) {
return distinctive.canStack(stackOne.getItemMeta(), stackTwo.getItemMeta());
}
return true;
}
return false;
} else if (item.hasItemMeta()) {
Debug.log(TestCase.CARGO_INPUT_TESTING, "SlimefunUtils#isItemSimilar - item.hasItemMeta()");
ItemMeta itemMeta = item.getItemMeta();
if (sfitem instanceof SlimefunItemStack) {
Optional<String> id = Slimefun.getItemDataService().getItemData(itemMeta);
String id = Slimefun.getItemDataService().getItemData(itemMeta).orElse(null);
if (id.isPresent()) {
return id.get().equals(((SlimefunItemStack) sfitem).getItemId());
if (id != null) {
if (checkDistinction) {
/*
* PR #3417
*
* Some items can't rely on just IDs matching and will implement {@link DistinctiveItem}
* in which case we want to use the method provided to compare
*/
Optional<DistinctiveItem> optionalDistinctive = getDistinctiveItem(id);
if (optionalDistinctive.isPresent()) {
ItemMeta sfItemMeta = sfitem.getItemMeta();
return optionalDistinctive.get().canStack(sfItemMeta, itemMeta);
}
}
return id.equals(((SlimefunItemStack) sfitem).getItemId());
}
ItemMetaSnapshot meta = ((SlimefunItemStack) sfitem).getItemMetaSnapshot();
@ -304,12 +388,25 @@ public final class SlimefunUtils {
* Slimefun items may be ItemStackWrapper's in the context of cargo
* so let's try to do an ID comparison before meta comparison
*/
ItemMeta possibleSfItemMeta = sfitem.getItemMeta();
Debug.log(TestCase.CARGO_INPUT_TESTING, " sfitem is ItemStackWrapper - possible SF Item: {}", sfitem);
ItemMeta possibleSfItemMeta = sfitem.getItemMeta();
String id = Slimefun.getItemDataService().getItemData(itemMeta).orElse(null);
String possibleItemId = Slimefun.getItemDataService().getItemData(possibleSfItemMeta).orElse(null);
// Prioritize SlimefunItem id comparison over ItemMeta comparison
if (Slimefun.getItemDataService().hasEqualItemData(possibleSfItemMeta, itemMeta)) {
if (id != null && id.equals(possibleItemId)) {
Debug.log(TestCase.CARGO_INPUT_TESTING, " Item IDs matched!");
/*
* PR #3417
*
* Some items can't rely on just IDs matching and will implement {@link DistinctiveItem}
* in which case we want to use the method provided to compare
*/
Optional<DistinctiveItem> optionalDistinctive = getDistinctiveItem(id);
if (optionalDistinctive.isPresent()) {
return optionalDistinctive.get().canStack(possibleSfItemMeta, itemMeta);
}
return true;
} else {
Debug.log(TestCase.CARGO_INPUT_TESTING, " Item IDs don't match, checking meta {} == {} (lore: {})", itemMeta, possibleSfItemMeta, checkLore);
@ -327,6 +424,14 @@ public final class SlimefunUtils {
}
}
private static @Nonnull Optional<DistinctiveItem> getDistinctiveItem(@Nonnull String id) {
SlimefunItem slimefunItem = SlimefunItem.getById(id);
if (slimefunItem instanceof DistinctiveItem distinctive) {
return Optional.of(distinctive);
}
return Optional.empty();
}
private static boolean equalsItemMeta(@Nonnull ItemMeta itemMeta, @Nonnull ItemMetaSnapshot itemMetaSnapshot, boolean checkLore) {
Optional<String> displayName = itemMetaSnapshot.getDisplayName();
@ -523,4 +628,27 @@ public final class SlimefunUtils {
public static @Nullable Item spawnItem(Location loc, ItemStack item, ItemSpawnReason reason) {
return spawnItem(loc, item, reason, false);
}
/**
* Helper method to check if an Inventory is empty (has no items in "storage").
* If the MC version is 1.16 or above
* this will call {@link Inventory#isEmpty()} (Which calls MC code resulting in a faster method).
*
* @param inventory
* The {@link Inventory} to check.
*
* @return True if the inventory is empty and false otherwise
*/
public static boolean isInventoryEmpty(@Nonnull Inventory inventory) {
if (Slimefun.getMinecraftVersion().isAtLeast(MinecraftVersion.MINECRAFT_1_16)) {
return inventory.isEmpty();
} else {
for (ItemStack is : inventory.getStorageContents()) {
if (is != null && !is.getType().isAir()) {
return false;
}
}
return true;
}
}
}