1
mirror of https://github.com/StarWishsama/Slimefun4.git synced 2024-09-20 11:45:51 +00:00

Added ItemFilter

This commit is contained in:
TheBusyBiscuit 2020-10-20 13:56:21 +02:00
parent 734b853eb7
commit aaec763a78
2 changed files with 178 additions and 3 deletions

View File

@ -32,8 +32,10 @@ import me.mrCookieSlime.Slimefun.api.item_transport.ItemTransportFlow;
final class CargoUtils { final class CargoUtils {
// Whitelist or blacklist slots /**
private static final int[] FILTER_SLOTS = { 19, 20, 21, 28, 29, 30, 37, 38, 39 }; * These are the slots where our filter items sit.
*/
static final int[] FILTER_SLOTS = { 19, 20, 21, 28, 29, 30, 37, 38, 39 };
private CargoUtils() {} private CargoUtils() {}
@ -391,7 +393,7 @@ final class CargoUtils {
} }
} }
// Check if there are event non-air items // Check if there are any non-air items
if (itemsToCompare > 0) { if (itemsToCompare > 0) {
// Only create the Wrapper if its worth it // Only create the Wrapper if its worth it
if (itemsToCompare > 1) { if (itemsToCompare > 1) {

View File

@ -0,0 +1,173 @@
package io.github.thebusybiscuit.slimefun4.core.networks.cargo;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import java.util.function.Predicate;
import javax.annotation.Nonnull;
import org.bukkit.Material;
import org.bukkit.block.Block;
import org.bukkit.inventory.ItemStack;
import io.github.thebusybiscuit.slimefun4.utils.SlimefunUtils;
import io.github.thebusybiscuit.slimefun4.utils.itemstack.ItemStackWrapper;
import me.mrCookieSlime.CSCoreLibPlugin.Configuration.Config;
import me.mrCookieSlime.Slimefun.Objects.SlimefunItem.SlimefunItem;
import me.mrCookieSlime.Slimefun.api.BlockStorage;
import me.mrCookieSlime.Slimefun.api.inventory.BlockMenu;
/**
* The {@link ItemFilter} is a performance-optimization for our {@link CargoNet}.
* It is a snapshot of a cargo node's configuration.
*
* @author TheBusyBiscuit
*
*/
class ItemFilter implements Predicate<ItemStack> {
/**
* Our {@link List} of items to check against, might be empty.
*/
private final List<ItemStackWrapper> items = new ArrayList<>();
/**
* Our default value for this {@link ItemFilter}.
* A default value of {@literal true} will mean that it returns true if no
* match was found. It will deny any items that match.
* A default value of {@literal false} means that it will return false if no
* match was found. Only items that match will make it past this {@link ItemFilter}.
*/
private boolean defaultValue;
/**
* Whether we should also compare the lore.
*/
private boolean checkLore;
/**
* If an {@link ItemFilter} is marked as dirty / outdated, then it will be updated
* on the next tick.
*/
private boolean dirty = false;
/**
* This creates a new {@link ItemFilter} for the given {@link Block}.
* This will copy all settings from that {@link Block} to this filter.
*
* @param b
* The {@link Block}
*/
public ItemFilter(@Nonnull Block b) {
update(b);
}
/**
* This updates or refreshes the {@link ItemFilter} to copy the settings
* from the given {@link Block}. It takes a new snapshot.
*
* @param b
* The {@link Block}
*/
public void update(@Nonnull Block b) {
// Store the returned Config instance to avoid heavy calls
Config blockData = BlockStorage.getLocationInfo(b.getLocation());
String id = blockData.getString("id");
SlimefunItem item = SlimefunItem.getByID(id);
BlockMenu menu = BlockStorage.getInventory(b.getLocation());
if (item == null || menu == null) {
// Don't filter for a non-existing item (safety check)
clear(false);
} else if (id.equals("CARGO_NODE_OUTPUT")) {
// Output Nodes have no filter, allow everything
clear(true);
} else {
this.items.clear();
this.checkLore = Objects.equals(blockData.getString("filter-lore"), "true");
this.defaultValue = !Objects.equals(blockData.getString("filter-type"), "whitelist");
for (int slot : CargoUtils.FILTER_SLOTS) {
ItemStack stack = menu.getItemInSlot(slot);
if (stack != null && stack.getType() != Material.AIR) {
this.items.add(new ItemStackWrapper(stack));
}
}
}
}
/**
* This will clear the {@link ItemFilter} and reject <strong>any</strong>
* {@link ItemStack}.
*
* @param defaultValue
* The new default value.
*/
private void clear(boolean defaultValue) {
this.items.clear();
this.checkLore = false;
this.defaultValue = defaultValue;
}
/**
* Whether this {@link ItemFilter} is outdated and needs to be refreshed.
*
* @return Whether the filter is outdated.
*/
public boolean isDirty() {
return dirty;
}
/**
* This marks this {@link ItemFilter} as dirty / outdated.
*/
public void markDirty() {
this.dirty = true;
}
@Override
public boolean test(@Nonnull ItemStack item) {
// An empty Filter does not need to be iterated over.
if (items.isEmpty()) {
return defaultValue;
}
int potentialMatches = 0;
// This is a first check for materials to see if we might even have any match.
// If there is no potential match then we won't need to perform the quite
// intense operation .getItemMeta()
for (ItemStackWrapper stack : items) {
if (stack.getType() == item.getType()) {
// We found a potential match based on the Material
potentialMatches++;
}
}
if (potentialMatches == 0) {
// If there is no match, we can safely assume the default value
return defaultValue;
} else {
// If there is more than one potential match, create a wrapper to save
// performance on the Item Meta otherwise just use the item directly.
ItemStack subject = potentialMatches == 1 ? item : new ItemStackWrapper(item);
// If there is only one match, we won't need to create a Wrapper
// and thus only perform .getItemMeta() once
for (ItemStackWrapper stack : items) {
if (SlimefunUtils.isItemSimilar(subject, stack, checkLore, false)) {
// The filter has found a match, we can return the opposite
// of our default value. If we exclude items, this is where we
// would return false. Otherwise we return true.
return !defaultValue;
}
}
// If no particular item was matched, we fallback to the default value.
return defaultValue;
}
}
}