1
mirror of https://github.com/StarWishsama/Slimefun4.git synced 2024-09-20 03:35:51 +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.Inventory;
import org.bukkit.inventory.ItemStack; 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.ItemGroup;
import io.github.thebusybiscuit.slimefun4.api.items.SlimefunItem; import io.github.thebusybiscuit.slimefun4.api.items.SlimefunItem;
import io.github.thebusybiscuit.slimefun4.api.items.SlimefunItemStack; import io.github.thebusybiscuit.slimefun4.api.items.SlimefunItemStack;
import io.github.thebusybiscuit.slimefun4.api.player.PlayerBackpack; import io.github.thebusybiscuit.slimefun4.api.player.PlayerBackpack;
import io.github.thebusybiscuit.slimefun4.api.recipes.RecipeType; 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.core.handlers.ItemUseHandler;
import io.github.thebusybiscuit.slimefun4.implementation.Slimefun; import io.github.thebusybiscuit.slimefun4.implementation.Slimefun;
import io.github.thebusybiscuit.slimefun4.implementation.items.SimpleSlimefunItem; import io.github.thebusybiscuit.slimefun4.implementation.items.SimpleSlimefunItem;
import io.github.thebusybiscuit.slimefun4.implementation.listeners.BackpackListener; import io.github.thebusybiscuit.slimefun4.implementation.listeners.BackpackListener;
import io.github.thebusybiscuit.slimefun4.utils.SlimefunUtils;
import io.github.thebusybiscuit.slimefun4.utils.tags.SlimefunTag; import io.github.thebusybiscuit.slimefun4.utils.tags.SlimefunTag;
/** /**
@ -28,7 +31,7 @@ import io.github.thebusybiscuit.slimefun4.utils.tags.SlimefunTag;
* @see PlayerBackpack * @see PlayerBackpack
* *
*/ */
public class SlimefunBackpack extends SimpleSlimefunItem<ItemUseHandler> { public class SlimefunBackpack extends SimpleSlimefunItem<ItemUseHandler> implements DistinctiveItem {
private final int size; 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) { private boolean isCraftable(Inventory inv, ItemStack[] recipe) {
for (int j = 0; j < inv.getContents().length; j++) { 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 (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; return false;
} }
} else { } 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.ItemSpawnReason;
import io.github.thebusybiscuit.slimefun4.api.items.SlimefunItem; import io.github.thebusybiscuit.slimefun4.api.items.SlimefunItem;
import io.github.thebusybiscuit.slimefun4.api.items.SlimefunItemStack; 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.Radioactive;
import io.github.thebusybiscuit.slimefun4.core.attributes.Soulbound; import io.github.thebusybiscuit.slimefun4.core.attributes.Soulbound;
import io.github.thebusybiscuit.slimefun4.core.debug.Debug; import io.github.thebusybiscuit.slimefun4.core.debug.Debug;
@ -260,7 +261,7 @@ public final class SlimefunUtils {
continue; continue;
} }
if (isItemSimilar(stack, item, checkLore, false)) { if (isItemSimilar(stack, item, checkLore, false, true)) {
return true; return true;
} }
} }
@ -268,11 +269,69 @@ public final class SlimefunUtils {
return false; 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) { 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) { 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) { if (item == null) {
return sfitem == null; return sfitem == null;
} else if (sfitem == null) { } else if (sfitem == null) {
@ -281,17 +340,42 @@ public final class SlimefunUtils {
return false; return false;
} else if (checkAmount && item.getAmount() < sfitem.getAmount()) { } else if (checkAmount && item.getAmount() < sfitem.getAmount()) {
return false; return false;
} else if (sfitem instanceof SlimefunItemStack && item instanceof SlimefunItemStack) { } else if (sfitem instanceof SlimefunItemStack stackOne && item instanceof SlimefunItemStack stackTwo) {
return ((SlimefunItemStack) item).getItemId().equals(((SlimefunItemStack) sfitem).getItemId()); 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()) { } else if (item.hasItemMeta()) {
Debug.log(TestCase.CARGO_INPUT_TESTING, "SlimefunUtils#isItemSimilar - item.hasItemMeta()"); Debug.log(TestCase.CARGO_INPUT_TESTING, "SlimefunUtils#isItemSimilar - item.hasItemMeta()");
ItemMeta itemMeta = item.getItemMeta(); ItemMeta itemMeta = item.getItemMeta();
if (sfitem instanceof SlimefunItemStack) { if (sfitem instanceof SlimefunItemStack) {
Optional<String> id = Slimefun.getItemDataService().getItemData(itemMeta); String id = Slimefun.getItemDataService().getItemData(itemMeta).orElse(null);
if (id.isPresent()) { if (id != null) {
return id.get().equals(((SlimefunItemStack) sfitem).getItemId()); 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(); ItemMetaSnapshot meta = ((SlimefunItemStack) sfitem).getItemMetaSnapshot();
@ -304,12 +388,25 @@ public final class SlimefunUtils {
* Slimefun items may be ItemStackWrapper's in the context of cargo * Slimefun items may be ItemStackWrapper's in the context of cargo
* so let's try to do an ID comparison before meta comparison * 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); 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 // 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!"); 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; return true;
} else { } else {
Debug.log(TestCase.CARGO_INPUT_TESTING, " Item IDs don't match, checking meta {} == {} (lore: {})", itemMeta, possibleSfItemMeta, checkLore); 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) { private static boolean equalsItemMeta(@Nonnull ItemMeta itemMeta, @Nonnull ItemMetaSnapshot itemMetaSnapshot, boolean checkLore) {
Optional<String> displayName = itemMetaSnapshot.getDisplayName(); Optional<String> displayName = itemMetaSnapshot.getDisplayName();
@ -523,4 +628,27 @@ public final class SlimefunUtils {
public static @Nullable Item spawnItem(Location loc, ItemStack item, ItemSpawnReason reason) { public static @Nullable Item spawnItem(Location loc, ItemStack item, ItemSpawnReason reason) {
return spawnItem(loc, item, reason, false); 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;
}
}
} }